转自: 如何基于protobuf实现一个极简版的RPC - Ying
1.介绍
pb是一种数据序列化方式。protobuf 实现了序列化部分,并且预留了 RPC 接口,但是没有实现网络交互的部分。
一次完整的 RPC 通信实际上是有三部分代码共同完成:
- protobuf 自动生成的代码
- RPC 框架
- 用户填充代码(Service的逻辑代码)
1.1 server端
server端需要继承并实现服务接口:
virtual void Echo(::google::protobuf::RpcController* controller,
const ::echo::EchoRequest* request,
::echo::EchoResponse* response,
::google::protobuf::Closure* done);
1.2 client端
客户端通过EchoService_Stub发送数据,EchoService_Stub::Echo
调用了::google::protobuf::Channel::CallMethod
,但是Channel
是一个纯虚类,需要 RPC 框架在子类里实现需要的功能。
// stub初始化时设置的数据成员
::google::protobuf::RpcChannel* channel_;
void EchoService_Stub::Echo(::google::protobuf::RpcController* controller,
const ::echo::EchoRequest* request,
::echo::EchoResponse* response,
::google::protobuf::Closure* done) {
channel_->CallMethod(descriptor()->method(0),
controller, request, response, done);
}
RpcChannel的声明:
class LIBPROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {}
virtual ~RpcChannel();
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller,
const Message* request,
Message* response,
Closure* done) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};
1.3 参数控制rpcController
其中服务端和客户端都会用到rpcController类,它是一个纯虚类,需要框架自己继承实现。
RpcController
是一个控制对象,负责控制 RPC 的状态、错误信息等。客户端可以通过它来控制和查询调用的状态。
class MyController : public ::google::protobuf::RpcController {
public:
// client端调用
virtual void Reset() { };
// client端调用判断服务运行结果
virtual bool Failed() const { return false; };
// 返回调用失败的原因,如果调用成功则返回空字符串
virtual std::string ErrorText() const { return ""; };
virtual void StartCancel() { };
// 服务端: 当调用过程中遇到错误时,设置失败原因
virtual void SetFailed(const std::string& /* reason */) { };
virtual bool IsCanceled() const { return false; };
virtual void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { };
};//MyController
有些 RPC 框架在具体实现中会扩展 RpcController
,例如添加流量控制或延迟信息的设置方法(如设置超时时间等)。
2.server端
考虑下 server 端的处理流程
- 从对端接收数据
- 通过标识机制判断如何反序列化到 request 数据类型
- 生成对应的 response 数据类型
- 调用对应的 service-method ,填充 response 数据
- 序列化 response
- 发送数据回对端
3.client端
rpcChannel 可以理解为一个通道,连接了 rpc 服务的两端,本质上也是通过 socket 通信的。RpcChannel
也是一个纯虚类,CallMethod = 0
。
因此我们需要实现一个子类,基类为RpcChannel
,并且实现CallMethod
方法,应该实现两个功能:
- 序列化 request ,发送到对端,同时需要标识机制使得对端知道如何解析(schema)和处理(method)这类数据。
- 接收对端数据,反序列化到 response
此外还有RpcController
,也是一个纯虚类,是一个辅助类,用于获取RPC结果,对端IP等。