本日我们来理解一下 Go 措辞是如何进行远程方法调用的,远程方法调用是做事间进行通信的根本办法之一,是 Go 措辞实现微做事架构必须节制的开拓知识和事理。
gRPCgRPC 是一个高性能、开源、通用的 RPC 框架,由 Google 推出,基于HTTP/2 协议标准设计开拓,默认采取 Protocol Buffers 数据序列化协议,支持多种开拓措辞。gRPC 供应了一种大略的方法来精确的定义做事,并且为客户端和做事端自动天生可靠代码的功能库。
我们来详细理解一下 gRPC 的浩瀚特性:
gRPC 利用 ProtoBuf 来定义做事、接口和数据类型,ProtoBuf 是由 Google 开拓的一种数据序列化协议(类似于XML、JSON和hessian)。ProtoBuf 能够将数据进行序列化,并广泛运用在数据存储和通信协议等方面。gRPC 支持多种措辞,并能够基于措辞自动天生客户端和做事端代码。gRPC支持 C、C++、Node.js、Python、Ruby、Objective-C、PHP和C# 等措辞,目前已供应了 C 措辞版本的 gRPC、Java 措辞版本的grpc-java 和 Go 措辞版本的 grpc-go,其他措辞的版本正在积极开拓中,个中,grpc-java 已经支持 Android 开拓。
如上图所示为 gRPC 的调用示意图,我们可以看到,一个 C++ 措辞的做事器可以通过 gRPC 分别与 Ruby 措辞开拓的桌面客户端和 Java 措辞开拓的 Android 客户端进行交互。
gRPC基于 HTTP/2 标准设计,以是相对付其他 RPC 框架,gRPC拥有更多强大功能,如双向流、头部压缩、多复用要求等。这些功能给移动设备带来重大益处,如节省带宽、降落 TCP 连接次数、提高CPU利用率和延长电池寿命等。同时,gRPC 还提高了云端做事和Web运用的性能。gRPC 既能够在客户端运用,也能够在做事器端运用,从而以透明的办法实现客户端和做事器真个通信和简化通信系统的构建。gRPC 的安装首先利用 go get 命令安装 grpc-go。
goget-ugoogle.golang.org/grpc
接着要安装插件,先利用 which protoc 命令检讨是否安装了protoc;如果没有,则利用go install命令安装 proto 和 protoc-gen-go 两个库,末了可以利用 protoc 方法判断是否成功安装了。
-----查看protoc是否安装,确保是3.0版本$whichprotoc$protoc--version-----安装插件$goinstallgithub.com/golang/protobuf/proto$goinstallgithub.com/golang/protobuf/protoc-gen-go-----测试是否安装成功$protoc-Ipb/string.proto--go_out=plugins=grpc:.pb/string.proto
gRPC 过程调用实践
gRPC 过程调用时,做事端和客户端须要依赖共同的 proto 文件。proto 文件可以定义远程调用的接口、方法名、参数和返回值等。通过 proto 文件可以自动天生客户端和客户真个相应 RPC 代码。借助这些代码,客户端可以十分方便地发送 RPC 要求,并且做事端也可以很大略地建立RPC做事器,处理RPC要求并且将返回值作为相应发送给客户端。
定义和编译 proto 文件首先,我们要定义一个 proto 文件,其详细语法请查看 Protobuf3 措辞指南。在该文件中,我们定义了两个参数结果,分别是 StringRequest 和 StringResponse,同时还有一个做事构造 StringService,代码如下:
syntax="proto3";packagepb;serviceStringService{rpcConcat(StringRequest)returns(StringResponse){}rpcDiff(StringRequest)returns(StringResponse){}}messageStringRequest{stringA=1;stringB=2;}messageStringResponse{stringRet=1;stringerr=2;}
StrtingService 有两个方法,分别为 Concat 和 Diff,每个方法都有对应的输入参数和返回值,这些值也都定义在 proto 文件中。
gRPC 可以定义4种类型的做事接口,分别是一元 RPC、做事器流 RPC、客户端流式 RPC 和双向流 RPC。
(1)一元 RPC 是指客户端向做事器发送要求并得到相应,就像正常的函数调用一样。
rpc Concat(StringRequest) returns (StringResponse) {}
(2)做事器流 RPC 是指客户端发送一个工具,做事器端返回一个Stream(流式)。
rpc LotsOfServerStream(StringRequest) returns (stream StringResponse) {}
(3)客户端流式 RPC,客户端发送一个 Stream(流式)做事端返回一个工具。
rpc LotsOfClientStream(stream StringRequest) returns (StringResponse) {}
(4)双向流 RPC,两个流独立运行,客户端和做事器可以按照它们喜好的顺序进行读取和写入;例如,做事器可以在写入相应之前等待吸收所有客户端,也可以交替地进行的读取和写入,或读取和写入的其他组合。每个流中的顺序被保留。类似于 WebSocket(长连接),客户端可以向做事端要求,做事器端也可以向客户端要求。
rpc LotsOfServerAndClientStream(stream StringRequest) returns (stream StringResponse) {}
接下来我们利用 protoc 编译工具编译这个protoc文件,天生做事端和客户真个代码,如下:
protoc --go_out=plugins=grpc:.pb/string.proto
从 proto 文件中的做事定义开始,gRPC 供应了天生客户机和做事器端代码的 protocol buffer 编译器插件。gRPC 用户常日在客户端调用这些 API,并在做事器端实现相应的 API。
在做事器端,做事器实现做事声明的方法,并运行 gRPC 做事器来处理客户端调用。gRPC 框架会接管网络传入要求,解析要求数据,实行相应做事方法和将方法结果编码成相应通过网络通报给客户端。客户真个本地定义方法,其方法名、参数和返回值与做事端定义的方法相同。客户端可以直接在本地工具上调用这些方法,将调用的参数包含在对应的 protocol buffer 类型中,gRPC再将要求发送到做事端,做事端解析要求。
客户端发送 RPC 要求我们先来看客户端代码,首先调用 grpc.Dial 建立网络连接,然后利用 protoc 编译天生的 pb.NewStringServiceClient 函数创建 gRPC 客户端,然后调用客户真个 Concat 函数,进行RPC调用,代码如下所示:
packagegrpcimport("context""fmt""github.com/keets2012/Micro-Go-Pracrise/ch9-rpc/pb""google.golang.org/grpc")funcmain(){serviceAddress:="127.0.0.1:1234"conn,err:=grpc.Dial(serviceAddress,grpc.WithInsecure())iferr!=nil{panic("connecterror")}deferconn.Close()stringClient:=pb.NewStringServiceClient(conn)stringReq:=&pb.StringRequest{A:"A",B:"B"}reply,_:=stringClient.Concat(context.Background(),stringReq)fmt.Printf("StringServiceConcat:%sconcat%s=%s",stringReq.A,stringReq.B,reply.Ret)}
做事端建立 RPC 做事
再来看看做事器真个代码,它首先须要调用 grpc.NewServer() 来建立RPC的做事端,然后将 StringService 注册到RPC做事端上,其具有的两个函数分别处理 Concat 和 Diff 要求,代码如下:
funcmain(){flag.Parse()lis,err:=net.Listen("tcp","127.0.0.1:1234")iferr!=nil{log.Fatalf("failedtolisten:%v",err)}grpcServer:=grpc.NewServer()stringService:=new(string_service.StringService)pb.RegisterStringServiceServer(grpcServer,stringService)grpcServer.Serve(lis)}
末了我们来看 StringService 的详细代码实现,它首先定义了StringService 构造体,然后实现了它的 Concat 方法和 Diff 方法。
typeStringServicestruct{}func(sStringService)Concat(ctxcontext.Context,reqpb.StringRequest)(pb.StringResponse,error){iflen(req.A)+len(req.B)>StrMaxSize{response:=pb.StringResponse{Ret:""}return&response,nil}response:=pb.StringResponse{Ret:req.A+req.B}return&response,nil}func(sStringService)Diff(ctxcontext.Context,reqpb.StringRequest)(pb.StringResponse,error){iflen(req.A)<1||len(req.B)<1{response:=pb.StringResponse{Ret:""}return&response,nil}res:=""iflen(req.A)>=len(req.B){for_,char:=rangereq.B{ifstrings.Contains(req.A,string(char)){res=res+string(char)}}}else{for_,char:=rangereq.A{ifstrings.Contains(req.B,string(char)){res=res+string(char)}}}response:=pb.StringResponse{Ret:res}return&response,nil}
如上代码所示,StringService 的 Concat 方法和 Diff 方法实现起来都很大略,Concat 方法便是将 StringRequest 中的 A 和 B 字符拼接在一起;而 Diff 方法则是通过循环遍历,将 A 和 B 字符的差异部分打算出来。
从上面的讲述可以看出,客户端发送一个要求后,必须等待做事器发回相应才能连续发送下一个要求,这种交互模式具有一定局限性,它无法更好地利用网络带宽,通报更多的要求或相应。而 gRPC 支持流式的要求相应模式来优化办理这一问题。