user.proto
syntax="proto3";
package fixbug;
option cc_generic_services=true;
message ResultCode
{
int32 errcode=1;
bytes errmsg=2;
}
message LoginRequest
{
bytes name=1;
bytes pwd=2;
}
message LoginResponse
{
ResultCode result=1;
bool success =2;
}
message RegisterRequest
{
uint32 id=1;
bytes name=2;
bytes pwd=3;
}
message RegisterResponse
{
ResultCode result=1;
bool success =2;
}
service UserServiceRpc
{
rpc Login(LoginRequest) returns(LoginResponse);
rpc Register(RegisterRequest) returns(RegisterResponse);
}
- 正如上一节所讲的,我们使用
protoc user.proto -I ./ -cpp_out=./user
命令来得到对应的user.pb.h
以及user.pb.cc
文件。 - user.cc和user.h里面提供了两个非常重要的类供c++程序使用,
UserServiceRpc_Stub
类给caller
使用,可以调用UserServiceRpc_Stub::Login(...)
发起远端调用UserServiceRpc
给callee
使用。callee则继承UserServiceRpc类并重写UserServiceRpc::Login(...)
函数,实现Login
函数的处理逻辑。- user.proto中也注册了通信的消息体(LoginRequest、LoginResponse、RegisterRequest、RegisterResponse(其中嵌套了ResultCode)),这些注册的消息体也会由protoc生成对应的C++类和业务代码进行交互。
userservice.cc
class UserService : public fixbug::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;
}
bool Register(uint32_t id,std::string name,std::string pwd)
{
std::cout<<"doing local service:Register"<<std::endl;
std::cout<<"id: "<<id<<"name: "<<name<<" pwd: "<<pwd<<std::endl;
return true;
}
void Login(::google::protobuf::RpcController *controller,
const ::fixbug::LoginRequest *request,
::fixbug::LoginResponse *response,
::google::protobuf::Closure *done)
{
//框架给业务上报了请求参数LoginRequest,应用获取相应数据做本地业务
std::string name=request->name();
std::string pwd=request->pwd();
//做本地业务
bool login_result=Login(name,pwd);
//把响应写入
fixbug::ResultCode *code=response->mutable_result();
code->set_errcode(0);
code->set_errmsg("");
response->set_success(login_result);
done->Run();
}
void Register(::google::protobuf::RpcController *controller,
const ::fixbug::RegisterRequest *request,
::fixbug::RegisterResponse *response,
::google::protobuf::Closure *done)
{
uint32_t id=request->id();
std::string name=request->name();
std::string pwd=request->pwd();
bool ret = Register(id,name,pwd);
response->mutable_result()->set_errcode(0);
response->mutable_result()->set_errmsg("");
response->set_success(ret);
done->Run();
}
};
- Userservice在不继承UserServiceRpc类的时候,它是一个本地服务,提供了两个进程内的本地方法,Login和GetFriendLists。
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;
}
bool Register(uint32_t id,std::string name,std::string pwd)
{
std::cout<<"doing local service:Register"<<std::endl;
std::cout<<"id: "<<id<<"name: "<<name<<" pwd: "<<pwd<<std::endl;
return true;
}
- 继承UserServiceRpc类,即
class UserService : public fixbug::UserServiceRpc
之后,我们将它实现为了rpc服务发布端,也就是rpc服务的提供者。 - 重写基类UserServiceRpc的虚函数caller = = > Login(LoginRequest) = => muduo = => callee = => Login(LoginRequest) = => 交到重写Login以及Register方法。
- 在Login以及Register方法中,主要实现框架给业务上报了请求参数
LoginRequest
,我们通过request
底层相关方法可以使应用获取相应数据做本地业务Login(name,pwd);
;然后写入相关的响应,执行回调操作,即执行响应对象的序列化和网络发送(这都是由框架来完成的)
main函数
int main(int argc,char **argv)
{
//调用框架初始化操作
MprpcApplication::Init(argc,argv);
RpcProvider provider;
provider.NotifyService(new UserService());
//启动一个rpc服务发布节点
provider.Run();
return 0;
}
- 在main函数中,首先调用框架初始化操作,这在
MprpcApplication
中会分析到; - 创建RpcProvider 对象,调用NotifyService方法,这是框架提供给外部使用的,可以发布rpc方法的函数接口,可以接受任意的service。
void NotifyService(google::protobuf::Service *service);
在RpcProvider
中会分析到; - 启动rpc服务节点,开始提供rpc远程网络调用服务,Run以后,进程进入阻塞状态,等待远程的rpc调用请求.
calluserservice.cc
int main(int argc,char **argv)
{
MprpcApplication::Init(argc,argv);
//演示调用远程发布的rpc方法的Login
fixbug::UserServiceRpc_Stub stub(new MprpcChannel());
//rpc方法的请求参数
fixbug::LoginRequest request;
request.set_name("zhang san");
request.set_pwd("123456");
//rpc方法的响应
fixbug::LoginResponse response;
MprpcController controller;
stub.Login(&controller,&request,&response,nullptr);
//一次rpc调用完成,读调用的结果
if (controller.Failed())
{
std::cout << controller.ErrorText() << std::endl;
}
else
{
if(0==response.result().errcode())
{
std::cout<<"rpc login response success: "<<
response.success()<<std::endl;
}
else
{
std::cout<<"rpc login response error: "<<
response.result().errmsg()<<std::endl;
}
}
MprpcController controller1;
if (controller.Failed())
{
std::cout << controller.ErrorText() << std::endl;
}
else
{
//演示调用远程发布的rpc方法的Register
fixbug::RegisterRequest req;
req.set_id(200);
req.set_name("mprpc");
req.set_pwd("6666");
fixbug::RegisterResponse rsp;
stub.Register(&controller1,&req,&rsp,nullptr);
if(0==rsp.result().errcode())
{
std::cout<<"Register login response success: "<<
rsp.success()<<std::endl;
}
else
{
std::cout<<"Register login response error: "<<
rsp.result().errmsg()<<std::endl;
}
}
return 0;
}
- 首先,callee在发起远程调用时,要先进行初始化,在MprpcApplication类中提供了解析argc和argv参数的方法,我们在终端执行这个程序的时候,需要通过
-i
参数给程序提供一个配置文件,这个配置文件里面包含了ip
以及port
。 - 实例化了一个UserServiceRpc_Stub对象,这个是我们在上一节提到的
.proto
中产生的最终的两个类其中之一,底层实际是通过继承google::protobuf::RpcChannel
实现了一个channel类,可以通过这个类解析到调用端需要的服务,然后通过网络进行服务实现。 - 接下来,通过LoginRequest,RegisterRequest给入rpc方法需要的参数,以及相应的响应类,对应的也是我们在
.proto
中给的rpc Login(LoginRequest) returns(LoginResponse);
。 - 将给入
UserServiceRpc_Stub
类,得到我们想要的响应结果。这个类第一个参数还需要一个controller值,这个我们将在后面提到。 0==rsp.result().errcode()
通过返回的错误码,我们可以判断响应结果是否是我们想要的。