Rpc服务消费者(Rpc服务调用者)实现思路
前面几节说到Rpc消费者主要通过UserServiceRPc_Stub这个protobuf帮我们生成的类来实现,上代码回顾一下
java
class UserServiceRpc_Stub : public UserServiceRpc {
public:
UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel);
UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel,
::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership);
~UserServiceRpc_Stub();
inline ::PROTOBUF_NAMESPACE_ID::RpcChannel* channel() { return channel_; }
// implements UserServiceRpc ------------------------------------------
void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done);
private:
::PROTOBUF_NAMESPACE_ID::RpcChannel* channel_;
bool owns_channel_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UserServiceRpc_Stub);
//xxx.pb.cc
void UserServiceRpc_Stub::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done) {
channel_->CallMethod(descriptor()->method(0),
controller, request, response, done);
}
};
UserServiceRpc_Stub可以看做是一个给用户提供rpc远程调用的代理类,这里面有rpcclient和rpcserver约定好的远程方法Login,Login方法是调用了一个channel_的callMethod方法,那么联想到其他服务方法应该也是底层调用了这个函数(底层根据method的索引来区分具体的method),那么这个方法具体如何工作的?这个类需要接受一个RpcChannel类作为参数,看看RpcChannel类:
java
class PROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {}
virtual ~RpcChannel();
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller, const Message* request,
Message* response, Closure* done) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};
很简单就是一个抽象类,有个纯虚函数CallMethod需要rpc_stub来实现。要实现rpcService_Stub,显然先需要具体化一个MyRpcChannel类,这个MyRpcChannel类继承自RpcChannel 类,最后通过多态调用在callMethod方法里面完成rpc远程调用的过程。那么具体怎么进行?
java
void UserServiceRpc_Stub::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done) {
channel_->CallMethod(descriptor()->method(0),
controller, request, response, done);
}
Login方法接收4个参数,第一个controller先不用管,显然第四个参数done也不需要管因为作为rpc消费者不需要在将什么结果反馈给rpc提供者。那么就只需要传递两个参数,request即是rpc_stub封装用户端请求的参数,response即是在远程调用完成之后rpcserver帮rpcclient填写的,然后rpc_stub只需要反序列化出来结果反馈给用户即可,那么基本实现思路就有了:
step1 :实现MyRpcchannel类,继承自RpcChannel ,并重写其CallMethod方法,CallMethod实现有如下几小步 :
step1.1:获取具体服务名和方法名以及将用户传入的request序列化,最终封装成一个字符流(header_size + service_name method_name args_size + args_str)
java
//获取服务名和方法名
const google::protobuf::ServiceDescriptor* service = method->service();
std::string service_name = service->name(); //service_name
std::string method_name = method->name(); //method_name
//获取参数的序列化字符串长度 args_size
std::string args_str;
uint32_t args_size = 0;
if(request->SerializeToString(&args_str))
{
args_size = args_str.size();
}
else
{
std::cout << "request serlize error" << std::endl;
return;
}
//定义rpc请求的Header
RpcHeader rpcHeader;
rpcHeader.set_service_name(service_name);
rpcHeader.set_method_name(method_name);
rpcHeader.set_args_size(args_size);
std::string rpc_header_str;
uint32_t header_size = 0;
if(rpcHeader.SerializeToString(&rpc_header_str))
{
header_size = rpc_header_str.size();
}
else
{
std::cout << "rpcheader_str serlize error" << std::endl;
return;
}
//组织待发送的rpc请求的字符串
std::string send_rpc_str;
send_rpc_str.insert(0, std::string((char*)&header_size, 4)); // header_size
send_rpc_str += rpc_header_str; //rpcHeader
send_rpc_str += args_str; //args
step1.2 :连接远程rpcServer服务器(可以是不同机器上的不同进程,也可以是在不同的机器上)
connect to rpcServer
step1.2 :将封装的rpc字符流通过socket发送给RpcServer(远程rpc调用请求)
send----->send_rpc_str
step1.3 :阻塞等待rpcServer的响应,并将响应饭序列化出来给用户端。
recv------>recv_buf
c
//反序列化rpc调用的响应数据
std::string response_str(recv_buf, 0, recv_size);
if(!response->ParseFromString(response_str))
{
std::cout<< "parse error ! response_str:" << response_str << std::endl;
return;
}
step2:用户创建UserServiceRpc_Stub类对象,并将MyRpcchannel实例对象传入进行构造。最后通过UserServiceRpc_Stub对象实例调用对应的rpc服务。(实际调用的就是上述实现的CallMethod方法)
c
fixbug::UserServiceRpc_Stub stub(new MyRpcChannel());
//rpc方法的请求参数
fixbug::LoginRequest request;
request.set_name("zhang san");
request.set_pwd("123");
//rpc方法的响应,同步的rpc调用过程
fixbug::LoginResponse response;
stub.Login(nullptr, &request, &response, nullptr); //RpcChannel-->Rpchannel::callMethod 集中来做所有rpc方法调用的参数序列化和网络发送
step3:处理阻塞的响应消息response做相应的业务处理。
c
//一次rpc调用完成,读调用的结果
if(0 == response.result().errcode())
{
std::cout << "rpc login response success: " << response.success() << std::endl;
}
else
{
std::cout << "rpc login error msg : " << response.result().errmsg() << std::endl;
}
至此RpcConsumer基本实现。