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