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了|

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


相关推荐
REDcker6 小时前
显示分辨率标准对照详解
前端·网络·分辨率·显示·屏幕
ANSIOT7 小时前
安朔科技蓝牙无感开锁方案,靠近自动解锁
网络·科技·物联网
晏宁科技YaningAI8 小时前
VoIP系统的工程实现模型:从信令控制到媒体传输的完整架构解析
网络·人工智能·架构·系统架构·信息与通信
wWYy.9 小时前
分布式:数据复制
分布式
神奇霸王龙11 小时前
Claude Code屠榜:MiMo与Grok紧追Codex
服务器·网络·人工智能·gpt·ai·ai编程
数智化管理手记13 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发13 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
RD_daoyi13 小时前
外链权重暴跌至13%,品牌提及反超传统链接——2026年不做Digital PR,你的独立站等于隐形
运维·网络·学习·机器学习·搜索引擎
张小姐的猫14 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
国科安芯14 小时前
星间光链路:AS32S601型抗辐射MCU在空间激光通信终端控制中的技术实现
服务器·网络·单片机·嵌入式硬件·物联网·安全·信息与通信