gRPC 概要笔记,包含序列化、protobuf、http/2、服务发现、负载均衡、流控。

gRPC

gRPC (Google Remote Procedure Call) 是由 Google 于 2015 年开源的高性能、通用的 RPC 框架。 其出现的背景是微服务通信激增、及跨语言通信等。[1]

序列化与反序列化

涉及 RPC,就必须依赖序列化与反序列化,以便在网络和各个服务中传递对象、数据交换等。

跨语言的序列化有如下几种:protobuf、thrift、hessian 等,gPRC 的默认序列化方案是 protobuf。

与 JSON 序列化或 Java 原生序列化相比,protobuf 有如下优势:

特性 Protobuf JSON Java 原生序列化
数据格式 二进制(紧凑) 文本(冗余) 二进制(极度冗余)
可读性 不可读(需工具还原) 可读 不可读
生成体积 极小 一般 极大
解析速度 极快 一般
语言支持 主流多语言支持 几乎任何语言 仅限 Java
Schema 约束 强类型(编译期检查) 弱类型 强类型(耦合高)

Java 序列化框架(Hession、Fastjson、Jackson、Gson)普遍存在反序列化漏洞,而 protobuf 通过限定类和显示调用,不存在此问题。[2]

protobuf

protobuf 是 gRPC 的默认序列化方案,但也是可选的。通过序列化与反序列化的对比,可以看到 protobuf 主要解决跨语言通信问题、类型统一且为强类型、二进制、体积及高效传输。

HTTP/2

gRPC 的应用层协议为 HTTP/2,利用了 HTTP/2 的较多新特性,包括:

  • 支持二进制分帧传输
  • 长连接,且多路复用,单个请求可处理多个请求和响应
  • 可通过流(Stream)和帧(Frame)实现并行传输
  • 支持流式双向通信

服务发现与负载均衡

gRPC 有较为简单的服务发现机制,内部默认通过 DNS 来做服务发现 [3],但由于 DNS 的 TTL 缓存机制,服务发现并没有那么及时,所以可以配合 xDS 等方案,配合 Envovy / Istio 等支持更为实时的服务发现。[5][6]

复杂均衡,是在服务发现后,如何选择具体哪台服务器进行调用,默认为 pick_first 策略,即选择第一个可用服务 [7]。也可自定义相关的负载均衡策略。

流量控制

这块是最为主要的实现逻辑,包括:认证机制、数据压缩、取消机制、超时机制、排队机制、健康检查、重试等。

一次简单请求的大致流程如下:

  • resolving_load_balancer 解析服务也有复杂均衡
    • dns_resolver DNS 解析器
      • resolving_call 解析操作
        • pick_first 选择第一个可用服务
  • channel 建立服务连接
    • subchannel 多路复用
      • retrying_call 叠加重试机制
        • subchannel_call 单个调用
  • transport 传输层
    • transport_internals 内部处理,包括保活、连接、代理等

以上步骤可通过官方样例查看 [8]

  • git clone --depth 1 --shallow-submodules https://github.com/grpc/grpc-node
  • cd grpc-node/examples
  • npm install
  • cd helloworld/dynamic_codegen
    • node greeter_server.js 开启服务
    • GRPC_NODE_VERBOSITY=DEBUG GRPC_NODE_TRACE=all node greeter_client.js 通过环境变量开启整体流程的观察

参考

  1. gRPC 官网
  2. Java反序列化漏洞总结
  3. Custom Name Resolution
  4. Dubbo 与 gRPC、Spring Cloud、Istio 的关系
  5. envoy xDS
  6. istio xDS
  7. Custom Load Balancing Policies
  8. gRPC node Quick start