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

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


相关推荐
橘颂TA5 小时前
【Linux】不允许你还不会——信号保存(3)
linux·服务器·网络·数据库
SETH·XU5 小时前
简记:关于net-snmp中engineid冲突全局etimelist的赋值情况
c语言·网络·net-snmp·snmpv3·engineid冲突
呼呼突突5 小时前
Unity使用TouchSocket的RPC
unity·rpc·游戏引擎
L、2185 小时前
深入实战:使用 Platform Channel 实现 Flutter 与 OpenHarmony 原生能力互通
分布式·flutter·harmonyos
sdszoe49225 小时前
思科DHCP+OSPF综合实验
运维·服务器·网络·ospf·思科dhcp
Cat God 0075 小时前
Kafka单机搭建(二)
分布式·kafka·linq
元气满满-樱5 小时前
Http概述
网络·网络协议·http
shjita5 小时前
hadoop运行jar包的相关配置参考!
大数据·hadoop·分布式
5 小时前
TIDB——TIKV——分布式事务与MVCC
数据库·分布式·tidb·分布式数据库·tikv·