基于 grpc 手撸一个 RPC 框架
1 手撸 RPC 框架的原因
常见的 RPC 框架有: 比较知名的如阿里的 Dubbo、google 的 gRPC、Go 语言的 rpcx。 但只有 gRPC 支持 streaming 模式。
gRPC 是在任何环境中运行的现代开源高性能 RPC 框架,基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
为了理解如何基于 gRPC 框架,开发 server/client 程序,所以基于 grpc 手撸一个 RPC 框架
 源代码存放在 gitee 上,欢迎大家围观:   https://gitee.com/cloudcoder/grpc-examples
1.1 gRPC 有什么好处
gRPC 和 restful API 都提供了一套通信机制,用于 server/client 模型通信,而且它们都使用 http 作为底层的传输协议(严格地说, gRPC 使用的 http2.0,而 restful api 则不一定)。
gRPC 还是有些特有的优势,如下:
- 通过 protobuf 定义协议接口,有更加严格的接口约束条件。 
- protobuf 定义的协议在传输时数据被序列化为二进制编码,大幅减少需要传输的数据量,节省带宽并性能。 
- gRPC 支持流式通信(理论上通过 http2.0 就可以使用 streaming 模式, 但是通常 web 服务的 restful api 很少这么用【WEB 服务基于 HTTP1.1】) 
- 支持多种语言 
 
 2. RPC 框架
2.1 RPC 介绍
RPC 就是要像调用本地的函数一样去调远程函数,RPC 过程如下:
 
 2.1.1 RPC 远程调用过程
- 客户机调用一个称为客户机存根【client stub】的本地过程。对于客户机进程来说,这似乎是实际的过程,因为它是一个常规的本地过程。client stub 将参数打包到远程过程(这可能涉及将它们转换为标准格式),并构建一个或多个网络消息。将参数打包到网络消息中称为封送处理【marshaling 】,并要求将所有数据序列化【serializing】为一种扁平的字节数组格式。 
- 网络消息由 client stub 发送到远程系统(通过使用 sockets interfaces 对本地内核【local kernel】的系统调用)。 
- 网络消息由内核通过某种协议(无连接或面向连接)传输到远程系统 
- 服务器存根【server stub】(有时称为骨架 skeleton)接收服务器上的消息。它从网络消息中 unmarshals 出参数,并在必要时将它们从标准网络格式转换为特定于机器的形式。 
- server stub 调用服务器函数(对客户机来说,服务器函数是远程过程),并将从客户机接收到的参数传递给它 
- 当服务器函数完成时,它将带着返回值返回到 server stub 
- 如果有必要,server stub 将返回值转换为一个或多个网络消息发送给 client stub 
- 消息通过网络发送回 client stub 
- client stub 从本地内核读取消息 
- 然后,client stub 将结果返回给客户机函数,如果需要,将它们从网络消息转换为本地的对象 
2.1.2 RPC 的好处
RPC 的主要好处有两方面。
- 程序员现在可以使用过程调用语义来调用远程函数并获取响应。 
- 编写分布式应用程序简化了,因为 RPC 将所有网络代码隐藏在存根函数中。应用程序不需要担心诸如套接字、端口号以及数据转换和解析等细节。 
在OSI参考模型上,RPC 跨越会话层和表示层(第 5 层和第 6 层)。
2.2 手撸 RPC 框架实现的功能
- 基于 NettyServerBuilder、NettyChannelBuilder,针对 RPC 的服务端、客户端进行了封装 
- 服务端支持 Authentication Interceptor 
- 服务端目前支持心跳服务、简单的 hello handler、client 管理 handler(提供 client 实例属性上报、keepalive 接口)、消息上报 handler, 通过 ServiceLoader 发现这些 handler 
- 客户端使用 ServiceManager 管理所有 BootService, 并在启动时,ServiceLoader 发现这些 Service,依次调用所有 BootService 实例的 prepare、startup、onComplete 方法,在 BootService 实例(除 GRPCChannelManager)外,将自己作为 GRPCChannelListener 注册到 GRPCChannelManager 中 
- 客户端使用 GRPCChannelManager 管理 GRPCChannel【针对 grpc 的 ManagedChannel 进行封装】,并启动一个线程,根据 channel 状态检查间隔时间定期检查是否需要重连 
- 客户端支持配置多个服务端,如果配置多个,则 client 随机绑定一个 
- 客户端连接成功后,会通知所有注册的服务,标识 channel 连接成功 
- 服务使用 channel,调用 rpc 服务 
2.2.1 服务端测试示例
2.2.2 客户端测试示例
客户端启动时序图
客户端启动时序图如下:
 
 服务端启动时序图
 
 3. 定义 rpc 协议
3.1 定义 rcp 协议
在 project 的 src/main/proto 和 src/test/proto,根据需要创建 proto 文件,如健康检查 proto
3.2 根据协议生成代码
使用的 protoc.version 版本是: 3.12.0
参考:https://www.grpc.io/docs/languages/java/generated-code/
在 pom.xml 文件中增加如下内容,则在 compile 或 package 时,protobuf-maven-plugin 会根据定义的 proto 文件,生成代码
默认情况下,生成的代码在:
- target/protobuf/grpc-java 存放生成的 rpc 服务,类以 Grpc 结尾 
- target/protobuf/grpc 存放生成的消息对象, 类名是消息的名称、消息的名称+OrBuilder 
版权声明: 本文为 InfoQ 作者【cloudcoder】的原创文章。
原文链接:【http://xie.infoq.cn/article/3f7ff15576fe34dfa2f089f1d】。文章转载请联系作者。











 
    
评论