参考资料
https://www.jianshu.com/p/9ea...
https://www.cnblogs.com/baosh...
http://doc.oschina.net/grpc?t...
https://pkg.go.dev/google.gol...
https://www.jianshu.com/p/b72...
https://grpc.io/docs/language...
https://www.cnblogs.com/ExMan...
https://blog.csdn.net/u011518...
https://grpc.io/docs/language...
https://www.cnblogs.com/aweso...
https://blog.csdn.net/zhangmi...
https://blog.csdn.net/xp17817...
1.概念
RPC
(remote procedure call
近程过程调用),实际上是提供了一套框架机制,使得位于网络中的不同机器上的应用程序之间能够进行通信互相调用,而且也听从server/client
模型。应用的时候客户端调用server
端提供的接口就像是调用本地的函数一样。通常RPC都是通过反射机制来实现的,本文不做深入分析,待后续文章再深入分析RPC的实现原理。
与其余的RPC框架相似,gRPC
在服务端提供一个gRPC Server
,客户端的库是gRPC Stub
。典型的场景是客户端发送申请,调用服务端的接口,客户端和服务端之间的通信协议是基于HTTP2的,反对双工的流式保序音讯,性能比拟好,同时也很轻量级。
2.长处
既然是vserver/client
模型,那么咱们间接用restful api
不是也能够的吗,为什么还须要RPC(或者gRPC)呢?上面咱们就来看看gRPC绝对于Restful API
到底有哪些劣势?gRPC和restful API
都提供了一套通信机制,用于server/client
模型通信,而且它们都应用http作为底层的传输协定。不过gRPC
还是有些特有的劣势的,如下:
- gRPC能够通过
protobuf
来定义接口,从而能够有更加严格的接口约束条件; - 另外,通过protobuf能够将数据序列化为二进制编码,这会缩小须要传输的数据量,从而进步性能;
- gRPC能够不便地反对流式通信(实践上通过
http2.0
就能够应用streaming
模式);
简略易学,疾速开始,可能反对多种语言和平台,双向流式通信、集成认证模块。
3.应用场景
须要对接口进行严格束缚的状况,咱们不心愿客户端给咱们传递任意的数据,尤其是思考到安全性的因素,咱们通常须要对接口进行更加严格的束缚。这时gRPC就能够通过protobuf来提供严格的接口束缚;
对于性能有更高的要求时。有时咱们的服务须要传递大量的数据,而又心愿不影响咱们的性能,这个时候也能够思考gRPC服务,因为通过protobuf
咱们能够将数据压缩编码转化为二进制格局,通常传递的数据量要小得多,而且通过http2
咱们能够实现异步的申请,从而大大提高了通信效率;
然而,通常咱们不会去独自应用gRPC
,而是将gRPC
作为一个部件进行应用,这是因为在生产环境,咱们面对大并发的状况下,须要应用分布式系统来去解决,而`gRPCv并没有提供分布式系统相干的一些必要组件。而且,真正的线上服务还须要提供包含负载平衡,限流熔断,监控报警,服务注册和发现等必要的组件;
接下来还得简略介绍一下Protobuf
,因为gRPC应用vprotobuf
来定义接口。Protobuf
是什么?Protobuf
理论是一套相似于Json或者XML的数据传输格局和标准,用于不同利用或过程之间进行通信时应用。通信时所传递的信息是通过Protobuf
定义的message
数据结构进行打包,而后编译成二进制的码流再进行传输或者存储。
4.Protobuf概念
Protobuf理论是一套相似于Json或者XML的数据传输格局和标准,用于不同利用或过程之间进行通信时应用。通信时所传递的信息是通过Protobuf
定义的message
数据结构进行打包,而后编译成二进制的码流再进行传输或者存储。
5.Protobuf长处
- 足够简略;
- 序列化后体积很小,音讯大小只须要
XML
的1/10 ~ 1/3
; - 解析速度快,解析速度比XML快
20 ~ 100
倍; - 多语言反对;
- 更好的兼容性,
Protobuf
设计的一个准则就是要可能很好的反对向下或向上兼容;
6.应用Protobuf步骤
- 定义音讯;
- 初始化音讯以及存储传输音讯;
- 读取音讯并解析;
Protobuf
的音讯构造是通过一种叫做Protocol Buffer Language
的语言进行定义和形容的,实际上Protocol Buffer Language
分为两个版本,版本2和版本3,默认不申明的状况下应用的是版本2,目前举荐应用的是版本3。
采纳ProtoBuf作为IDL
(Interface Definition Language
接口定义语言),须要定义service
和message
,生成客户端和服务端代码。用户本人实现服务端代码中的调用接口,并且利用客户端代码来发动申请到服务端。service
代表RPC接口,message
代表数据结构(外面能够包含不同类型的成员变量,包含字符串、数字、数组、字典等)。message
中成员变量前面的数字代表进行二进制编码时候的提示信息,1~15
示意热变量,会用较少的字节来编码。默认所有变量都是可选的(optional
),repeated
则示意数组。service rpc
接口只能承受单个message
参数,返回单个message。
7.golang装置gRpc
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpcgit clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/netgit clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/textgo get -u github.com/golang/protobuf/{proto,protoc-gen-go}git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto cd $GOPATH/src/go install google.golang.org/grpc
windows装置:
$ export GO111MODULE=on # Enable module mode$ go get google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc$ export PATH="$PATH:$(go env GOPATH)/bin"
问题:
【1】 go get github.com/golang/protobuf/protoc-gen-go的问题
解决链接:https://blog.csdn.net/wwqcher...
【2】 git呈现fatal: The remote end hung up unexpectedly
计划:https://blog.csdn.net/qq_3539...
办法:git clone --depth 1 https://github.com/grpc/grpc-...
8.例子
https://www.sohu.com/a/426454...
对于开发者而言:
【1】须要应用protobuf
定义接口,即.proto
文件
【2】而后应用compile
工具生成特定语言的执行代码,比方JAVA
、C/C++
、Python
等。相似于thrift
,为了解决跨语言问题。
【3】启动一个Server
端,server
端通过侦听指定的port
,来期待Client链接申请,通常应用Netty
来构建,GRPC
内置了Netty
的反对。
【4】启动一个或者多个Client
端,Client
也是基于Netty,Client通过与Server建设TCP
长链接,并发送申请;Request
与Response
均被封装成HTTP2的stream Frame
,通过Netty Channel进行交互。
8.1 定义服务
咱们想要实现的是通过gRPC框架进行近程服务调用,首先第一步应该是要有服务。利用之前所把握的内容,gRPC框架反对对服务的定义和生成。gRPC框架默认应用protocol buffers
作为接口定义语言,用于形容网络传输音讯构造。除此之外,还能够应用protobuf
定义服务接口。
syntax = "proto3";package message;//订单申请参数message OrderRequest { string orderId = 1; int64 timeStamp = 2;}//订单信息message OrderInfo { string OrderId = 1; string OrderName = 2; string OrderStatus = 3;}//订单服务service定义service OrderService{ rpc GetOrderInfo(OrderRequest) returns (OrderInfo);}
通过proto文件定义了数据结构的同时,还定义了要实现的服务接口,GetOrderInfo
即是具体服务接口的定义,在GetOrderInfo
接口定义中,OrderRequest
示意是申请传递的参数,OrderInfo
示意处理结果返回数据参数。
8.2 环境筹备
定义的proto文件须要通过编译,生成go语言代码文件,供客户端程序和服务端程序应用。能够装置go语言环境中的对于proto的插件。
go get -a github.com/golang/protobuf/protoc-gen-go #-a 参数标示下载好后间接做 go install
能够通过根本编译命令实现对.proto文件的编译.根底编译命令如下:
protoc --go_out=. *.proto
gRPC编译反对
如果定义的.proto
文件,如本案例中所示,定义中蕴含了服务接口的定义,而咱们想要应用gRPC框架实现RPC调用。开发者能够采纳protocol-gen-go
库提供的插件编译性能,生成兼容gRPC框架的golang语言代码。只须要在根本编译命令的根底上,指定插件的参数,告知protoc
编译器即可。具体的编译生成兼容gRPC框架的服务代码的命令如下:
protoc --go_out=plugins=grpc:. *.proto
8.3 gRPC实现RPC编程
8.3.1 服务接口实现
在.proto
定义好服务接口并生成对应的go语言文件后,须要对服务接口做具体的实现。定义服务接口具体由OrderServiceImpl
进行实现,并实现GetOrderInfo
具体内容,服务实现逻辑与前文所述内容雷同。不同点是服务接口参数的变动。具体代码实现如下:
type OrderServiceImpl struct {}//具体的办法实现func (os *OrderServiceImpl) GetOrderInfo(ctx context.Context, request *message.OrderRequest) (*message.OrderInfo, error) { orderMap := map[string]message.OrderInfo{ "201907300001": message.OrderInfo{OrderId: "201907300001", OrderName: "衣服", OrderStatus: "已付款"}, "201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "零食", OrderStatus: "已付款"}, "201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "食品", OrderStatus: "未付款"}, } var response *message.OrderInfo current := time.Now().Unix() if (request.TimeStamp > current) { *response = message.OrderInfo{OrderId: "0", OrderName: "", OrderStatus: "订单信息异样"} } else { result := orderMap[request.OrderId] if result.OrderId != "" { fmt.Println(result) return &result, nil } else { return nil, errors.New("server error") } } return response, nil}
8.3.2 gRPC实现服务端
应用gRPC框架,首先实现服务端的程序。既然应用gRPC
框架来实现,就须要调用gRPC进行服务办法的注册以及监听的解决。服务注册和监听解决实现如下:
func main() { server := grpc.NewServer() message.RegisterOrderServiceServer(server, new(OrderServiceImpl)) lis, err := net.Listen("tcp", ":8090") if err != nil { panic(err.Error()) } server.Serve(lis)}
8.3.3 gRPC实现客户端
实现完服务端当前,实现客户端程序。和服务端程序关系对应,调用gRPC框架的办法获取相应的客户端程序,并实现服务的调用,具体编程实现如下:
func main() { //1、Dail连贯 conn, err := grpc.Dial("localhost:8090", grpc.WithInsecure()) if err != nil { panic(err.Error()) } defer conn.Close() orderServiceClient := message.NewOrderServiceClient(conn) orderRequest := &message.OrderRequest{OrderId: "201907300001", TimeStamp: time.Now().Unix()} orderInfo, err := orderServiceClient.GetOrderInfo(context.Background(), orderRequest) if orderInfo != nil { fmt.Println(orderInfo.GetOrderId()) fmt.Println(orderInfo.GetOrderName()) fmt.Println(orderInfo.GetOrderStatus()) }}
运行程序
通过上述步骤后,程序及逻辑全副开发实现。程序运行,打印如下后果:
201907300001衣服已付款
9.生成.go文件
$ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld/helloworld.proto
go_out
:生成*.pb.go
代码的门路go-grpc_out
:生产*.grpc.pb.go
的门路helloworld/helloworld.proto
:*.proto
的门路
10.遇到的谬误
【1】Missing 'go_package' option Go 生成 grpc文件告警
https://www.jianshu.com/p/e05...
【2】No syntax specified for the proto file : xxx.proto
https://blog.csdn.net/weixin_...
【3】proto:2:1: Interpreting non ascii codepoint 226.
https://blog.csdn.net/qq_3845...
https://www.jianshu.com/p/6f1...
【4】 rpc error: code = Unimplemented desc = RPC method not implemented
https://www.cnblogs.com/lavin...
【5】 _grpc.pb.go:14:11: undefined: grpc.SupportPackageIsVersion7
https://blog.csdn.net/yzf2795...
【6】 *.pb.go:222:7: undefined: grpc.ClientConnInterface
https://blog.csdn.net/qq_1607...
在go mod
中批改版本
syntax = "proto3";// 协定为proto3option go_package = ".;protoTest";// 定义发送申请信息message SimpleRequest{ // 定义发送的参数 // 参数类型 参数名 标识号(不可反复) string data = 1;}// 定义响应信息message SimpleResponse{ // 定义接管的参数 // 参数类型 参数名 标识号(不可反复) int32 code = 1; string value = 2;}// 定义咱们的服务(可定义多个服务,每个服务可定义多个接口)service Simple{ rpc Route (SimpleRequest) returns (SimpleResponse){};}