服务器之家:专注于服务器技术及软件下载分享
分类导航

服务器资讯|IT/互联网|云计算|区块链|软件资讯|操作系统|手机数码|百科知识|免费资源|头条新闻|

服务器之家 - 新闻资讯 - 云计算 - 五分钟学会 gRPC,你学会了吗?

五分钟学会 gRPC,你学会了吗?

2022-03-08 22:36crossoverJie 云计算

gRPC 内容还是非常多的,本文只是作为一份入门资料希望能让不了解 gRPC 的能有一个基本认识;这在云原生时代确实是一门必备技能。

介绍

我猜测大部分长期使用 Java 的开发者应该较少会接触 gRPC,毕竟在 Java 圈子里大部分使用的还是 Dubbo/SpringClound 这两类服务框架。

我也是近段时间有机会从零开始重构业务才接触到 gRPC 的,当时选择gRPC 时也有几个原因:

五分钟学会 gRPC,你学会了吗?

  • 基于云原生的思路开发部署项目,而在云原生中 gRPC 几乎已经是标准的通讯协议了。
  • 开发语言选择了 Go,在 Go 圈子中 gRPC 显然是更好的选择。
  • 公司内部有部分业务使用的是 Python 开发,在多语言兼容性上 gRPC 支持的非常好。

经过线上一年多的平稳运行,可以看出 gRPC 还是非常稳定高效的;rpc 框架中最核心的几个要点:

  • 序列化
  • 通信协议
  • IDL(接口描述语言)

这些在 gRPC 中分别对应的是:

  • 基于 Protocol Buffer 序列化协议,性能高效。
  • 基于 HTTP/2 标准协议开发,自带 stream、多路复用等特性;同时由于是标准协议,第三方工具的兼容性会更好(比如负载均衡、监控等)
  • 编写一份 .proto 接口文件,便可生成常用语言代码。

HTTP/2

学习 gRPC 之前首先得知道它是通过什么协议通信的,我们日常不管是开发还是应用基本上接触到最多的还是 HTTP/1.1 协议。

五分钟学会 gRPC,你学会了吗?

由于 HTTP/1.1 是一个文本协议,对人类非常友好,相反的对机器性能就比较低。

需要反复对文本进行解析,效率自然就低了;要对机器更友好就得采用二进制,HTTP/2 自然做到了。

除此之外还有其他优点:

  • 多路复用:可以并行的收发消息,互不影响
  • HPACK 节省 header 空间,避免 HTTP1.1 对相同的 header 反复发送。

Protocol

gRPC 采用的是 Protocol 序列化,发布时间比 gRPC 早一些,所以也不仅只用于 gRPC,任何需要序列化 IO 操作的场景都可以使用它。

它会更加的省空间、高性能;之前在开发 https://github.com/crossoverJie/cim 时就使用它来做数据交互。

package order.v1; service OrderService{ rpc Create(OrderApiCreate) returns (Order) {} rpc Close(CloseApiCreate) returns (Order) {} // 服务端推送
  rpc ServerStream(OrderApiCreate) returns (stream Order) {} // 客户端推送
  rpc ClientStream(stream OrderApiCreate) returns (Order) {} // 双向推送
  rpc BdStream(stream OrderApiCreate) returns (stream Order) {} } message OrderApiCreate{ int64 order_id = 1; repeated int64 user_id = 2; string remark = 3; repeated int32 reason_id = 4; } 

使用起来也是非常简单的,只需要定义自己的 .proto 文件,便可用命令行工具生成对应语言的 SDK。

具体可以参考官方文档:https://grpc.io/docs/languages/go/generated-code/

调用

 protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ test.proto 

五分钟学会 gRPC,你学会了吗?

生成代码之后编写服务端就非常简单了,只需要实现生成的接口即可。

func (o *Order) Create(ctx context.Context, in *v1.OrderApiCreate) (*v1.Order, error) { // 获取 metadata
 md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Errorf(codes.DataLoss, "failed to get metadata") } fmt.Println(md) fmt.Println(in.OrderId) return &v1.Order{ OrderId: in.OrderId, Reason: nil, }, nil } 

五分钟学会 gRPC,你学会了吗?

客户端也非常简单,只需要依赖服务端代码,创建一个 connection 然后就和调用本地方法一样了。

这是经典的 unary(一元)调用,类似于 http 的请求响应模式,一个请求对应一次响应。

五分钟学会 gRPC,你学会了吗?

Server stream

gRPC 除了常规的 unary 调用之外还支持服务端推送,在一些特定场景下还是很有用的。

五分钟学会 gRPC,你学会了吗?

func (o *Order) ServerStream(in *v1.OrderApiCreate, rs v1.OrderService_ServerStreamServer) error { for i := 0; i < 5; i++ { rs.Send(&v1.Order{ OrderId: in.OrderId, Reason: nil, }) } return nil } 

服务端的推送如上所示,调用 Send 函数便可向客户端推送。

 for { msg, err := rpc.RecvMsg() if err == io.EOF { marshalIndent, _ := json.MarshalIndent(msgs, "", "\t") fmt.Println(msg) return } } 

客户端则通过一个循环判断当前接收到的数据包是否已经截止来获取服务端消息。

为了能更直观的展示这个过程,优化了之前开发的一个 gRPC 客户端,可以直观的调试 stream 调用。

五分钟学会 gRPC,你学会了吗?

上图便是一个服务端推送示例。

Client Stream

五分钟学会 gRPC,你学会了吗?

除了支持服务端推送之外,客户端也支持。

客户端在同一个连接中一直向服务端发送数据,服务端可以并行处理消息。

// 服务端代码
func (o *Order) ClientStream(rs v1.OrderService_ClientStreamServer) error { var value []int64
 for { recv, err := rs.Recv() if err == io.EOF { rs.SendAndClose(&v1.Order{ OrderId: 100, Reason: nil, }) log.Println(value) return nil } value = append(value, recv.OrderId) log.Printf("ClientStream receiv msg %v", recv.OrderId) } log.Println("ClientStream finish") return nil } // 客户端代码
 for i := 0; i < 5; i++ { messages, _ := GetMsg(data) rpc.SendMsg(messages[0]) } receive, err := rpc.CloseAndReceive() 

代码与服务端推送类似,只是角色互换了。

五分钟学会 gRPC,你学会了吗?

Bidirectional Stream

五分钟学会 gRPC,你学会了吗?

同理,当客户端、服务端同时都在发送消息也是支持的。

// 服务端
func (o *Order) BdStream(rs v1.OrderService_BdStreamServer) error { var value []int64
 for { recv, err := rs.Recv() if err == io.EOF { log.Println(value) return nil } if err != nil { panic(err) } value = append(value, recv.OrderId) log.Printf("BdStream receiv msg %v", recv.OrderId) rs.SendMsg(&v1.Order{ OrderId: recv.OrderId, Reason: nil, }) } return nil } // 客户端
 for i := 0; i < 5; i++ { messages, _ := GetMsg(data) // 发送消息
  rpc.SendMsg(messages[0]) // 接收消息
  receive, _ := rpc.RecvMsg() marshalIndent, _ := json.MarshalIndent(receive, "", "\t") fmt.Println(string(marshalIndent)) } rpc.CloseSend() 

其实就是将上诉两则合二为一。

五分钟学会 gRPC,你学会了吗?

通过调用示例很容易理解。

元数据

gRPC 也支持元数据传输,类似于 HTTP 中的 header。

 // 客户端写入
 metaStr := `{"lang":"zh"}`
 var m map[string]string
 err := json.Unmarshal([]byte(metaStr), &m) md := metadata.New(m) // 调用时将 ctx 传入即可
 ctx := metadata.NewOutgoingContext(context.Background(), md) // 服务端接收
 md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Errorf(codes.DataLoss, "failed to get metadata") } fmt.Println(md) 

gRPC gateway

gRPC 虽然功能强大使用也很简单,但对于浏览器、APP的支持还是不如 REST 应用广泛(浏览器也支持,但应用非常少)。

为此社区便创建了 https://github.com/grpc-ecosystem/grpc-gateway 项目,可以将 gRPC 服务暴露为 RESTFUL API。

五分钟学会 gRPC,你学会了吗?

为了让测试可以习惯用 postman 进行接口测试,我们也将 gRPC 服务代理出去,更方便的进行测试。

反射调用

作为一个 rpc 框架,泛化调用也是必须支持的,可以方便开发配套工具;gRPC 是通过反射支持的,通过拿到服务名称、pb 文件进行反射调用。

https://github.com/jhump/protoreflect 这个库封装了常见的反射操作。

上图中看到的可视化 stream 调用也是通过这个库实现的。

负载均衡

由于 gRPC 是基于 HTTP/2 实现的,客户端和服务端会保持长连接;这时做负载均衡就不像是 HTTP 那样简单了。

而我们使用 gRPC 想达到效果和 HTTP 是一样的,需要对请求进行负载均衡而不是连接。

通常有两种做法:

  • 客户端负载均衡
  • 服务端负载均衡

客户端负载均衡在 rpc 调用中应用广泛,比如 Dubbo 就是使用的客户端负载均衡。

gRPC 中也提供有相关接口,具体可以参考官方demo。

https://github.com/grpc/grpc-go/blob/87eb5b7502/examples/features/load_balancing/README.md

客户端负载均衡相对来说对开发者更灵活(可以自定义适合自己的策略),但相对的也需要自己维护这块逻辑,如果有多种语言那就得维护多份。

所以在云原生这个大基调下,更推荐使用服务端负载均衡。

可选方案有:

  • istio
  • envoy
  • apix

这块我们也在研究,大概率会使用 envoy/istio。

总结

gRPC 内容还是非常多的,本文只是作为一份入门资料希望能让不了解 gRPC 的能有一个基本认识;这在云原生时代确实是一门必备技能。

对文中的 gRPC 客户端感兴趣的朋友,可以参考这里的源码:https://github.com/crossoverJie/ptg。

延伸 · 阅读

精彩推荐
  • 云计算微软与印度信实工业子公司将在云计算等领域进行合作

    微软与印度信实工业子公司将在云计算等领域进行合作

    【TechWeb】8月13日消息,据国外媒体报道,印度信实工业旗下子公司将同微软进行长期的合作,涉及云计算、人工智能等多个领域。 信实工业公司已在官网...

    Techweb3542019-08-14
  • 云计算Kubernetes 将弃用 Docker

    Kubernetes 将弃用 Docker

    近日,Kubernetes 官方发布公告,宣布自 v1.20 起放弃对 Docker 的支持,届时用户将收到 Docker 弃用警告,并需要改用其他容器运行时。但 Docker 作为容器镜像构...

    开源中国4312020-12-03
  • 云计算企业需要了解的十个云安全优秀实践

    企业需要了解的十个云安全优秀实践

    IT 安全社区因其共享信息和解决方案的特性而备受认可。如果企业正在迁移到云端,或者倾向于提高应用程序的安全性,可以很好地利用这一点。...

    云联壹云9592022-02-17
  • 云计算加快边缘计算发展的十大趋势

    加快边缘计算发展的十大趋势

    在边缘计算中,数据处理发生在网络边缘而不是集中式中心。这意味着网络边缘的设备需要具有处理和存储功能。实际上,边缘计算有许多不同的形式。企...

    企业网D1Net2672020-10-20
  • 云计算云原生开发将成为高级部署蓝图

    云原生开发将成为高级部署蓝图

    如今,有些文章介绍了云原生开发蓝图的基础和探索逻辑图,并探讨了在内部部署设施和远程容器上进行云原生开发的用例以及部署蓝图。...

    企业网D1Net2852020-07-12
  • 云计算关于云原生安全,你得关注这些事!

    关于云原生安全,你得关注这些事!

    变化是唯一不变的,也是不可避免的。一段时间以来,云技术领域的 IT 格局发生了变化。企业正在转向云原生领域和容器、Kubernetes和无服务器框架。...

    新钛云服11442021-09-28
  • 云计算使用虚拟私有云的利弊

    使用虚拟私有云的利弊

    虚拟私有云(VPC)实际上是公共云提供商的营销响应,很多提供商发现自己与多年前就已经与OpenStack等私有云竞争。一些企业并不喜欢将数据放置在公共云...

    机房3605782020-06-05
  • 云计算Rancher 2.7.2 发布,开放的可互操作性再升级

    Rancher 2.7.2 发布,开放的可互操作性再升级

    Rancher 2.7.2 正式发布,主要提升了扩展性,以大幅强化生态能力。此外,作为 Rancher 的商用企业订阅版,Rancher Prime 进一步强化了其企业订阅价值;Rancher De...

    未知732023-05-06