GRPC 是一种二进制,性能好,跨语言,还灵活,同时可以进行服务治理的多快好省的 RPC 框架,唯一的不足是要写协议文件。
GRPC 序列化使用 Protocol Buffers,网络传输使用 HTTP 2.0,服务治理可以使用基于 Envoy 的 Service Mesh。
Protocol Buffers
GRPC 要同时做到使用二进制、跨语言,需要双方定义一个协议文件 .proto,规定好双方沟通的专业术语。
以购买专栏为例:
syntax = “proto3”; # 使用 proto3 语法
package com.geektime.grpc
option java_package = “com.geektime.grpc”;
message Order { # 消息类型,发出去的参数
required string date = 1; # 字段都有唯一的标识,压缩时只需传输数字标识即可
required string classname = 2;
required string author = 3;
required int price = 4;
}
message OrderResponse { # 消息类型,返回的结果
required string message = 1;
}
service PurchaseOrder { # 定义 Service
rpc Purchase (Order) returns (OrderResponse) {} # RPC 调用
}
类型中的字段都有唯一的标识,压缩时只需传输数字标识即可。
无论使用什么语言,都有相应的工具生成客户端和服务端的 Stub 程序,这样客户端就可以像调用本地一样调用远程服务。
协议约定问题
Protocol Buffers 是一款压缩率极高的序列化协议。对于 int 类型,Protocol Buffers 使用变长整数形式。
每一个 Byte 的 8 位,最高位都有特殊的含义:
- 为 1,表示后续的 Byte 也属于该数。
- 为 0,该数到此结束。
每个字段,使用 TLV(Tag, Length, Value)存储方式。其中,Tag = (field_num << 3) | wire_type。field_num 是在 proto 文件中,给每个字段指定的唯一数字标识,wire_type 标识后面的数据类型。
例如,对于 string author = 3,field_num 为 3,string 的 wire_type 为 2,有 11 << 3 | 10 = 11000 | 10 = 11010 = 26;Value 为 “liuchao”,如果使用 UTF-8 编码,长度为 7 个字符。所以,最终存储形式为(26, 7, liuchao)。在序列化效率方面做到了极致。
在灵活性方面,这种基于协议文件的二进制压缩协议存在更新不便的问题。Protocol Buffers 考虑了兼容性。协议文件中的每一个字段都有修饰符,如:
- required:该值不能为空。
- optional:可选字段,不设置时使用默认值。
- repeated:可重复 0 到多次。
网络传输问题
如果时 Java 技术栈,GRPC 的客户端和服务器之间通过 Netty Channel 作为数据通道,每个请求被封装称 HTTP 2.0 的 Stream。
Netty 是一个高效的基于异步 IO 的网络传输框架。
HTTP 2.0 的传输:
由于基于 HTTP 2.0,GRPC 和 其他 RPC 不同,可定义四种服务方法。
1、最常用的单向 RPC,客户端像服务端发送一个请求,并获取一个应答,就像一次普通函数的调用。
rpc SayHello(HelloRequest) returns (HelloResponse){}
2、服务端流式 RPC,服务端返回一批结果。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){}
3、客户端流式 RPC,客户端发出一批请求。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {}
4、双向流式 RPC,两边分别通过一个读写数据流来发送一系列消息。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){}
基于 HTTP 2.0,客户端和服务端之间的交互方式比较丰富,不仅可以单向远程调用,还可实现当服务端状态改变时,主动通知客户端。
服务发现和治理问题
GRPC 本身没有提供服务发现机制,需借助 Envoy。Envoy 不仅是负载均衡器,还是高性能的 C++ 写的 Proxy 转发器,可以配置非常灵活的转发规则。
规则可以是静态的,放在配置文件中,启动时加载。Envoy 支持热加载和热重启。
最好将规则设置为动态的,放在统一的地方维护,这个统一的地方是服务发现(Discovery Service)。
无论是静态配置、还是动态配置,往往包含 4 项:
- listener,监听端口。
- endpoint,目标 IP 和端口。
- cluster,是具有完全相同行为的多个 endpoint。从 cluster 到 endpoint 的过程为负载均衡。
- route,有时多个 cluster 有类似的功能,但版本号不同,可通过 route 规则将请求路由到某一版本号,即某个 cluster。
服务中心需要实现 Envoy 的 API,Envoy 可主动去服务发现中心拉取转发策略。Envoy 在控制面和服务发现中心沟通时,使用 GRPC。
Envoy 配置规则:
- 配置路由策略。例如后端服务有 2 个版本,可通过配置 Envoy 的 route,设置两个版本间,即 两个 cluster 间的 route 规则,一个占 99% 的流量,另一个占 1%。
- 负载均衡策略。对于一个 cluster 下的多个 endpoint,可配置负载均衡机制和健康检查机制。
节点的变化均会上传到注册中心,策略都可通过注册中心下发,所以,注册中心可称为注册管理中心。
未来服务治理的趋势 Service Mesh,应用之间的相互调用、服务之间的治理完全由 Envoy 代理,将服务治理抽象到平台层解决。