MPRPC项目(第七天,rpcprovider分发rpc服务)

这里使用到的是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,参数都是一样的,所以需要这些

虽然参数格式固定,但 requestresponse 的具体类型会根据 .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了|

后面再阅读一下,应该还能补充一些 |


相关推荐
ALex_zry3 小时前
C++网络编程心跳机制与连接保活:长连接稳定性保障
开发语言·网络·c++
@insist1235 小时前
网络工程师-信道容量计算与 PCM 编码:数据通信核心原理及软考考点解析
网络·网络工程师·pcm·软考·软件水平考试
桌面运维家6 小时前
VHD/VHDX 数据守护:BAT位图校验与修复
linux·服务器·网络
Dylan~~~7 小时前
深度解析Cassandra:分布式数据库的王者之路
数据库·分布式
F1FJJ7 小时前
Shield CLI Postgres v0.3.10:当 142 张表挤在一张 ER 图里,我们做了什么
网络·vscode·网络协议·postgresql·开源软件
Hello_Embed8 小时前
嵌入式上位机开发入门(四):TCP 编程 —— Client 端实现
网络·笔记·网络协议·tcp/ip·嵌入式
小雨青年8 小时前
鸿蒙 HarmonyOS 6 | Video 组件网络视频播放异常排查实战
网络·音视频·harmonyos
网硕互联的小客服8 小时前
CentOS系统如何卸载桌面并以shell 字符界面启动?
运维·服务器·网络·安全
传感器与混合集成电路10 小时前
面向储气库注采井的分布式光纤监测技术
分布式
攻城狮在此10 小时前
华三中小型企业二层组网配置案例一(单ISP+单链路)
网络·华为