这里使用到的是OnMessage函数,在rpcprovider.h的rpcprovider类下
cpp
//读写已建立连接用户的读写事件回调
void OnMessage(const muduo::net::TcpConnectionPtr&,muduo::net::Buffer*,muduo::Timestamp);
一、protobuf通信类型
在框架内,rpcprovider和rpcconsumer之间协商好了protobuf通信类型
先4字节头,是后面服务名和服务方法名以及方法参数名的长度,然后通过这个就能得到服务方法参数
17userservicelogin10 wang123456,这个17占四字节
在src目录下,新建rpcheader.proto文件
cpp
syntax = "proto3";
package mprpc;
message RpcHeader{
bytes service_name = 1;
bytes method_name = 2;
uint32 args_size = 3;
}
在一开始的user.proto文件里定义了
cpp
message LoginRequest{
bytes name = 1;
bytes password = 2;
}
这样,就能获取完整信息了
Protobuf 序列化RpcHeader对象时,会生成一串二进制位序列(比如0100000101110101...);
计算机自动把这串二进制位按每 8 位拆分,封装成一个个字节(比如01000001变成 1 个字节,值为 65;01110101变成 1 个字节,值为 117);
二、OnMessage函数
在网络请求到达时,进入OnMessage函数,然后在这里获得一系列参数,最后再通过callmethod(这里是框架自动调用在注册表上的服务方法),比如这里执行callmethod就是相当于调用了Login
先上目前完整代码
cpp
void RpcProvider::OnMessage(const muduo::net::TcpConnectionPtr& conn,muduo::net::Buffer* buffer,muduo::Timestamp){
//通过rpc接收客户端调用请求的字符流
std::string recv_buf = buffer->retrieveAllAsString();
//从字符流中读取前四个字节
uint32_t header_size = 0;
recv_buf.copy((char *)& header_size,4,0);
//根据header_size读取数据的原始字符流,并反序列数据,得到rpc请求详细信息
std::string rpc_header_str = recv_buf.substr(4,header_size);
std::string service_name;
std::string method_name;
uint32_t args_size = 0;
mprpc::RpcHeader rpcHeader;
//反序列化rpc请求头
if(rpcHeader.ParseFromString(rpc_header_str)){
service_name = rpcHeader.service_name();
method_name = rpcHeader.method_name();
args_size = rpcHeader.args_size();
std::cout<<"反序列化rpc请求头成功"<<std::endl;
}else{
std::cout<<rpc_header_str<<"反序列化rpc请求头失败"<<std::endl;
return;
}
//获取服务方法的参数
std::string args_str = recv_buf.substr(4+header_size,args_size);
//打印测试
std::cout<<"rpc_header_str: "<<rpc_header_str<<std::endl;
std::cout<<"header_size: "<<header_size<<std::endl;
std::cout<<"service_name: "<<service_name<<std::endl;
std::cout<<"method_name: "<<method_name<<std::endl;
std::cout<<"args_str: "<<args_str<<std::endl;
//获取service对象和method对象
auto it = m_serviceMap.find(service_name);
if(it == m_serviceMap.end()){
std::cout<<"service_name: "<<service_name<<" not found"<<std::endl;
return;
}
auto mit = it->second.m_methodMap.find(method_name);
if(mit == it->second.m_methodMap.end()){
std::cout<<"method_name: "<<method_name<<" not found"<<std::endl;
return;
}
//获取service对象和method对象
google::protobuf::Service* service = it->second.m_service;
const google::protobuf::MethodDescriptor* method = mit->second;
//生成rpc方法调用的请求request和响应response参数
google::protobuf::Message* request = service->GetRequestPrototype(method).New();
if(!request->ParseFromString(args_str)){
std::cout<<"request parse error"<<std::endl;
return;
}
google::protobuf::Message* response = service->GetResponsePrototype(method).New();
//给下面method方法调用绑定一个Closure回调函数
google::protobuf::Closure *done = google::protobuf::NewCallback<RpcProvider,
const muduo::net::TcpConnectionPtr&,
google::protobuf::Message*>
(this,&RpcProvider::SendRpcResponse,conn,response);
//框架根据远端rpc请求,调用当前rpc节点上发布(注册)的服务对象上的服务方法
service->CallMethod(method,NULL,request,response,done);
}
Login,这是一个重写的
cpp
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;
}
//重写基类UserServiecRpc中的纯虚函数,下面这些方法都是框架直接调用的。
//这里我们是服务端,对端寻求服务时,框架会帮我们调用这些方法,会进行匹配然后再经过框架返回回去
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->password();
//调用本地服务
bool request_result = Login(name,pwd);
//设置响应结果
fixbug::ResultCode* code = response->mutable_result();
code->set_errcode(0);
code->set_errmsg("");
response->set_success(request_result);
//执行回调 执行响应对象数据的序列化和网络发送(都是由框架来做的)
done->Run();
}
在Login里done->Run()并不是我写的run函数,而是Closure下自带的(这里一开始没想起来,迷惑好久),用于回调后面的setresponse()。
1、先通过buffer->retrieveAllAsString()获得客户端调用的请求字符流
cpp
//通过rpc接收客户端调用请求的字符流
std::string recv_buf = buffer->retrieveAllAsString();
2、再通过copy读取前四个字节
cpp
uint32_t header_size = 0;
recv_buf.copy((char *)& header_size,4,0);
3、然后再获得请求的详细信息
cpp
std::string rpc_header_str = recv_buf.substr(4,header_size);
std::string service_name;
std::string method_name;
uint32_t args_size = 0;
mprpc::RpcHeader rpcHeader;
//反序列化rpc请求头
if(rpcHeader.ParseFromString(rpc_header_str)){
service_name = rpcHeader.service_name();
method_name = rpcHeader.method_name();
args_size = rpcHeader.args_size();
std::cout<<"反序列化rpc请求头成功"<<std::endl;
}else{
std::cout<<rpc_header_str<<"反序列化rpc请求头失败"<<std::endl;
return;
}
//获取服务方法的参数
std::string args_str = recv_buf.substr(4+header_size,args_size);
我的疑惑这里方法参数是否要进行拆分是否要根据实际情况而定,还是会自动拆分
这里网课老师没有做拆分(我应该没漏听啊)
4、从表里查询服务对象和方法对象
cpp
//获取service对象和method对象
auto it = m_serviceMap.find(service_name);
if(it == m_serviceMap.end()){
std::cout<<"service_name: "<<service_name<<" not found"<<std::endl;
return;
}
auto mit = it->second.m_methodMap.find(method_name);
if(mit == it->second.m_methodMap.end()){
std::cout<<"method_name: "<<method_name<<" not found"<<std::endl;
return;
}
5、获取service对象和method对象
cpp
google::protobuf::Service* service = it->second.m_service;
const google::protobuf::MethodDescriptor* method = mit->second;
6、生成rpc方法调用的请求request和响应response参数
所有重写的功能比如这里的Login,参数都是一样的,所以需要这些
虽然参数格式固定,但 request 和 response 的具体类型会根据 .proto 文件定义变化。
cpp
google::protobuf::Message* request = service->GetRequestPrototype(method).New();
if(!request->ParseFromString(args_str)){
std::cout<<"request parse error"<<std::endl;
return;
}
google::protobuf::Message* response = service->GetResponsePrototype(method).New();
7、绑定回调函数
这里回调函数调用的就是SendResponse函数
cpp
google::protobuf::Closure *done = google::protobuf::NewCallback<RpcProvider,
const muduo::net::TcpConnectionPtr&,
google::protobuf::Message*>
(this,&RpcProvider::SendRpcResponse,conn,response);
8、调用服务端发布的服务方法
这里相当于Login
cpp
service->CallMethod(method,NULL,request,response,done);
三、SendResponse函数
在rpcprovider.h里定义了
cpp
//回调操作,用于序列化rpc的响应和网络发送
void SendRpcResponse(const muduo::net::TcpConnectionPtr&,google::protobuf::Message* response);
在rpcprovider.cc里实现
cpp
//Closure回调操作,用于序列化rpc的响应和网络发送
void RpcProvider::SendRpcResponse(const muduo::net::TcpConnectionPtr& conn,google::protobuf::Message* response){
std::string response_str;
if(response->SerializeToString(&response_str)){
//序列化成功,把执行结果返回给rpc调用方
conn->send(response_str);
}else{
std::cout<<"response serialize error"<<std::endl;
}
conn->shutdown();//模拟http短链接服务,由rpcprovider主动断开连接
}
目前客户端还没实现,无法做测试 0:55了|
后面再阅读一下,应该还能补充一些 |