RPC框架GPRC入门浅析

RPC框架中数据的传输通常有两种:二进制传输,和文本类传输 。
二进制传输的优点是:传输性能好,因为要写协议文件,所以更严谨。
缺点是:二进制难以跨语言,

文本类传输的优点是:可以跨语言,而且由于不用写协议文件,使用更灵活 。
缺点是:传输性能稍微要差一些。

GRPC的特点是不仅采用了二进制传输,保证了传输性能,还满足跨语言,保证了灵活性。

序列化

GPRC的二进制序列化协议是Protocol Buffers。
Protocol Bufers 是一款压缩效率极高的序列化协议,有很多设计精巧的序列化方法。
对于 int 类型 32 位的,一般都需要 4 个 Byte 进行存储。在 Protocol Bufers 中,使用的是变长整数的形式。对于每一个 Byte 的 8 位,最高位都有特殊的含义。

如果该位为 1 ,表示这个数字没完,后续的 Byte 也属于这个数字;如果该位为 0 ,则这个数字到此结束。其他的 7 个 Bit才是用来表示数字的内容。因此,小于 128 的数字都可以用一个 Byte 表示;大于 128 的数字,比如 130 ,会用两个字节来表示。

对于每一个字段,使用的是 TLV ( Tag , Length , Value )的存储办法。
其中 Tag = (feld_num << 3) | wire_type 。 feld_num 就是在 proto 文件中,给每个字段指定唯一的数字标识,而 wire_type 用于标识后面的数据类型。

网络传输

如果是 Java 技术栈, GRPC 的客户端和服务器之间通过 Netty Channel 作为数据通道,每个请求都被封装成 HTTP 2.0 的 Stream 。Netty是一个高效的基于异步IO的网络传输框架

HTTP 2.0协议将一个TCP的连接,切分成多个流,每个流都
有自己的 ID ,而且流是有优先级的。流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道。
HTTP 2.0 还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。
通过这两种机制, HTTP 2.0 的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新
组装,并且可以根据优先级,决定优先处理哪个流的数据。

由于基于 HTTP 2.0 , GRPC 和其他的 RPC 不同,可以定义四种服务方法。
第一种,也是最常用的方式是 单向RPC ,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。

rpc SayHello(HelloReques) returns (HelloResponse){}

第二种方式是 服务端流式RPC ,即服务端返回的不是一个结果,而是一批。客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读
取,直到没有更多消息为止。

rpc LotsOfReplies(HelloReques) returns (sream HelloResponse){}

第三种方式为 客户端流式RPC ,也即客户端的请求不是一个,而是一批。客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这
些消息并返回应答。

rpc LotsOfGreetings(sream HelloReques) returns (HelloResponse) {}

第四种方式为 双向流式 RPC ,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,服务端
可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者读写相结合的其他方式。每个数据流里消息的顺序会被保持。

rpc BidiHello(sream HelloReques) returns (sream HelloResponse){}

如果基于 HTTP 2.0 ,客户端和服务器之间的交互方式要丰富得多,不仅可以单方向远程调用,还可以实现当服务端状态改变的时候,主动通知客户端。
至此,传输问题得到了解决。

服务治理

GRPC 本身没有提供服务发现的机制,需要借助其他的组件,发现要访问的服务端,在多个服务端之间进行容错和负载均衡。
其实负载均衡本身比较简单, LVS 、 HAProxy 、 Nginx 都可以做,关键问题是如何发现服务端,并根据服务端的变化,动态修改负载均衡器的配置。

有一种对于 GRPC 支持比较好的负载均衡器 Envoy 。其实 Envoy 不仅仅是负载均衡器,它还是一个高性能的 C++ 写的 Proxy 转发器,可以配置非常灵活的转发规则。
这些规则可以是静态的,放在配置文件中的,在启动的时候加载。要想重新加载,一般需要重新启动,但是 Envoy 支持热加载和热重启,这在一定程度上缓解了这个问题。
当然,最好的方式是将规则设置为动态的,放在统一的地方维护。这个统一的地方在 Envoy 眼中被称为服务发现( Discovery Service ),过一段时间去这里拿一下配置,就修改了
转发策略。
无论是静态的,还是动态的,在配置里面往往会配置四个东西。
第一个是 listener 。 Envoy 既然是 Proxy ,专门做转发,就得监听一个端口,接入请求,然后才能够根据策略转发,这个监听的端口就称为 listener 。
第二个是 endpoint ,是目标的 IP 地址和端口。这个是 Proxy 最终将请求转发到的地方。
第三个是 cluster 。一个 cluster 是具有完全相同行为的多个 endpoint ,也即如果有三个服务端在运行,就会有三个 IP 和端口,但是部署的是完全相同的三个服务,它们组成一
个 cluster ,从 cluster 到 endpoint 的过程称为负载均衡,可以轮询。
第四个是 route 。有时候多个 cluster 具有类似的功能,但是是不同的版本号,可以通过 route 规则,选择将请求路由到某一个版本号,也即某一个 cluster 。
如果是静态的,则将后端的服务端的 IP 地址拿到,然后放在配置文件里面就可以了。

参考资料

极客时间的《趣谈网络协议》

你可能感兴趣的:(计算机网络)