基于 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】。文章转载请联系作者。
评论