到目前为止,我们的理论知识基本足够,当开始编码阶段时,我们第一个要考虑的问题就是如何将一个本地的方法注册成RPC服务方法,从而被远程调用。
一、在proto文件中添加服务
因为全局使用了protobuf作为数据的序列化和反序列化工具,所以需要先在proto文件中定义以下信息:
syntax ="proto3";//定义proto的最低版本
package RPC;//定义生成的namespace
option cc_generic_services=true;//指定将service也生成为类
message ResultCode//消息类型
{
int32 errcode=1;
bytes errmsg=2;
}
message LoginRequest
{
bytes name=1;
bytes pwd=2;
}
message LoginResponse
{
ResultCode result=1;
bool success=2;
}
service UserServiceRpc//定义服务类型,
{
rpc Login(LoginRequest)returns(LoginResponse);
}
在proto文件中我们使用了message和service两个关键词组,在编译proto文件时会将他们对应的内容都生成为相应的类,例如:UserService原来是一个本地服务,提供了进程内的本地方法:Login(),在proto文件中需要先在service类中注册该方法(要在proto文件中加上option cc_generic_services=true以将service下的方法生成为C++类的代码)。然后对proto文件进行编译生成后会生成对应的.pb.h和.pb.cc文件,其中会将proto文件注册的service直接生成为对应的类。
proto文件编译指令:protoc -user.proto --cpp_out=./
可以打开对应的user.pb.h文件查看是否生成了对应的类和方法:
可以看到service已经生成了类,而他对应的Login方法也已经生成了虚函数(生成虚函数的作用下边再说)。
二、注册为RPC方法
在我们要使用注册方法的文件中包含生成的.pb.h文件,书写新的service类,这个新的类需要继承proto文件生成的基类UserServiceRpc,对基类虚函数进行重写,即可将自己写的方法注册为RPC的方法:
class UserService:public RPC::UserServiceRpc
{
public:
//本地方法,模拟做本地业务
bool Login(std::string name,std::string pwd);
//重写的Login方法,用于接收、处理和返回本地业务对应数据
void Login(::google::protobuf::RpcController* controller,
const ::RPC::LoginRequest* request,
::RPC::LoginResponse* response,
::google::protobuf::Closure* done);
};
Login方法参数:
(1)::google::protobuf::RpcController* controller:此参数会携带一些其他的信息,用于做RPC远程方法的其他控制;
(2)::RPC::LoginRequest* request:就是RPC远程调用的请求参数;
(3)::RPC::LoginResponse* response:RPC远程调用完成后此参数用于返回结束后的数据;
(4)::google::protobuf::Closure* done:执行回调操作,用于执行响应对象数据的序列化和网络发送。
三、调用本地服务
在调用本地服务前我们先看下上面两个方法的实现
class UserService:public RPC::UserServiceRpc
{
public:
//登入系统的方法
bool Login(std::string name,std::string pwd)
{
std::cout<<"doing local service:Login"<<std::endl;
std::cout<<"name:"<<name<<" pwd"<<pwd<<std::endl;
return true;
}
void Login(::google::protobuf::RpcController* controller,
const ::RPC::LoginRequest* request,
::RPC::LoginResponse* response,
::google::protobuf::Closure* done)
{
//框架给业务上报了请求参数:LoginRequest,应用程序取出相应的已反序列化的数据来做本地业务
std::string name=request->name();
std::string pwd=request->pwd();
//做本地业务
bool loginresult=Login(name,pwd);
//把响应写入,包括错误码,错误信息和运行结果
RPC::ResultCode *Code=response->mutable_result();
Code->set_errcode(0);
Code->set_errmsg("");
response->set_success(loginresult);
//执行回调操作 执行响应对象数据的序列化和网络发送
done->Run();
}
};
Login是重写的虚函数,所以在发起远程请求后会调用这个方法来完成对应的目标请求,其过程为:
1、从LoginRequest获取参数的值;
2、执行本地服务Login,并获取返回值;
3、用上面的返回值填写LoginResponse;
4、一个回调,把LoginResonse发送给发起RPC服务的主机。
重写基类UserServiceRpc的虚函数,以下方法由框架直接调用:
1.caller远程调用者发起远程调用请求Login(LoginRequest) -> muduo -> callee
2.callee发现远程请求调用Login(LoginRequest) -> 交付给重写的方法即可
项目持续更新中……