MPRPC项目(第八天,(消费者视角)RpcChannel的调用过程以及实现)

一、实现目标

这里实现的就是左边黑圈里的过程:

开始调用->序列化->发送->接收响应结果->反序列化

右边的_stub是专门给rpc服务调用者来使用的,想要调用rpc服务,也应该用在user.proto定义好的

  • 定义了一个名为 Login 的远程过程调用方法
  • 输入参数:LoginRequest - 包含登录请求信息
  • 返回值:LoginResponse - 包含登录响应结果
cpp 复制代码
service UserServiceRpc{
    // Login handles user authentication with name and password
    rpc Login(LoginRequest) returns(LoginResponse);
}

二、caller下calluserservice.cc实现

cpp 复制代码
#include<iostream>
#include "user.pb.h"
#include"mprpcapplication.h"
#include"mprpcchannel.h"

int main(int argc,char **argv){
    //整个程序启动后,想用mprpc框架调用rpc服务,一定要先调用框架的初始化函数(只初始化一次)
    MprpcApplication::Init(argc,argv);

    //演示远程调用发布的rpc方法 Login
    fixbug::UserServiceRpc_Stub stub(new MprpcChannel());
    //stub.Login();

    //rpc方法的请求参数
    fixbug::LoginRequest request;
    request.set_name("zhang san");
    request.set_password("123456");

    //RPC方法的响应
    fixbug::LoginResponse response;
    
    //发起rpc方法的调用  同步的rpc调用过程  MprpcChannel::callmethod
    stub.Login(nullptr, &request, &response, nullptr);

    //一次rpc调用完整,读取调用结果
    //0表示成功,非0表示错误    如404,not found
    if(response.result().errcode() == 0){
        std::cout<<"rpc login resonse success: "<<response.success()<<std::endl;
    }else{
        std::cout<<"rpc login resonse failed: "<<response.result().errcode()<<" msg: "<<response.result().errmsg()<<std::endl;
    }

    return 0;
}

1、要先初始化整个rpc框架

cpp 复制代码
MprpcApplication::Init(argc,argv);
  • 初始化RPC应用程序
  • 解析命令行参数获取配置文件路径
  • 加载配置文件中的服务器IP、端口等配置信息

2、调用

cpp 复制代码
//演示远程调用发布的rpc方法 Login
    fixbug::UserServiceRpc_Stub stub(new MprpcChannel());
    //stub.Login();

    //rpc方法的请求参数
    fixbug::LoginRequest request;
    request.set_name("zhang san");
    request.set_password("123456");

    //RPC方法的响应
    fixbug::LoginResponse response;
    
    //发起rpc方法的调用  同步的rpc调用过程  MprpcChannel::callmethod
    stub.Login(nullptr, &request, &response, nullptr);
  1. fixbug::UserServiceRpc_Stub 就像是外卖平台的客户端APP

  2. stub 就是你手机上打开的外卖APP

  3. MprpcChannel() 就像是你的手机网络(4G/5G/WiFi)

最上面就是"打开外卖APP,连接到网络",最下面就是下单点餐。

3、获取响应结果

cpp 复制代码
 //一次rpc调用完整,读取调用结果
    //0表示成功,非0表示错误    如404,not found
    if(response.result().errcode() == 0){
        std::cout<<"rpc login resonse success: "<<response.success()<<std::endl;
    }else{
        std::cout<<"rpc login resonse failed: "<<response.result().errcode()<<" msg: "<<response.result().errmsg()<<std::endl;
    }

三、mprpcchannel.h

cpp 复制代码
#pragma once

#include<google/protobuf/service.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

class MprpcChannel:public google::protobuf::RpcChannel{
public:
    void CallMethod(const google::protobuf::MethodDescriptor* method,
                     google::protobuf::RpcController* controller,
                     const google::protobuf::Message* request,
                     google::protobuf::Message* response,
                     google::protobuf::Closure* done
    );
};

这里定义一个MprpcChannel类,继承google::protobuf::RpcChannel类,实现该虚函数

不管调用什么方法如这里stub.Login(),都会转换为对callmethod这一个方法调用

比如这里第一个参数就是Login

四、mprpcchannel.cc

该函数实现

1、先获取所有

2、将请求的rpc参数组装 数组的序列化(进行序列化后依然是string类型,cout遇到可打印的ASCLL字符就打印 )

3、发送rpc请求 wait

4、接收rpc响应 反序列化

先上完整代码

cpp 复制代码
#include "mprpcchannel.h"
#include <string>
#include "rpcheader.pb.h"
#include <sys/types.h>         
#include <sys/socket.h>
#include <error.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "mprpcapplication.h"
#include <unistd.h>

/*
rpc请求的数组组装   数组的序列化
发送rpc请求 wait
接收rpc响应   反序列化
*/
void MprpcChannel::CallMethod(const google::protobuf::MethodDescriptor* method,
                                google::protobuf::RpcController* controller,
                                const google::protobuf::Message* request,
                                google::protobuf::Message* response,
                                google::protobuf::Closure* done){
    //header_size +service_name + method_name + args_size + args_str
    const google::protobuf::ServiceDescriptor* sd = method->service();
    std::string service_name = sd->name();
    std::string method_name = method->name();

    //获取参数的序列化字符串长度 args_size
    int args_size = 0;
    std::string args_str;
    if(!request->SerializeToString(&args_str)){
        std::cout<<"serialize request failed"<<args_str<<std::endl;
        return;
    }else{
        args_size = args_str.size();
    }

    //定义rpc的请求header
    mprpc::RpcHeader rpcHeader;
    rpcHeader.set_service_name(service_name);
    rpcHeader.set_method_name(method_name);
    rpcHeader.set_args_size(args_size);

    //将header序列化
    uint32_t header_size = 0;
    std::string rpc_header_str;
    if(rpcHeader.SerializeToString(&rpc_header_str)){
        header_size = rpc_header_str.size();
        
    }else{
        std::cout<<"serialize header failed"<<std::endl;
        return;
    }
    
    //拼接发送的rpc请求字符串
    std::string send_rpc_str;
    send_rpc_str.insert(0,std::string((char *)&header_size,4));
    send_rpc_str += rpc_header_str;
    send_rpc_str += args_str;

    //打印测试
    std::cout<<"=============================="<<std::endl;
    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;
    std::cout<<"send_rpc_str: "<<send_rpc_str<<std::endl;
    std::cout<<"=============================="<<std::endl;

    //使用tcp编程完成rpc方法的远程调用
    int clientfd = socket(AF_INET,SOCK_STREAM,0);
    if(clientfd == -1){
        std::cout<<"create socket failed"<<errno<<std::endl;
        return;
    }

    //设置服务端的ip和port
    struct sockaddr_in server_addr;
    std::string ip = MprpcApplication::GetInstance().GetConfig().Load("rpcserverip");
    uint16_t port = atoi(MprpcApplication::GetInstance().GetConfig().Load("rpcserverport").c_str());
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip.c_str());

    //连接rpc服务节点
    int ret = connect(clientfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    if(ret == -1){
        std::cout<<"connect failed"<<errno<<std::endl;
        close(clientfd);
        exit(EXIT_FAILURE);
    }

    //发送rpc请求
    if(send(clientfd,send_rpc_str.c_str(),send_rpc_str.size(),0) == -1){
        std::cout<<"send rpc request failed"<<errno<<std::endl;
        return;
    }

    //接收rpc响应
    char recv_buf[1024] = {0};
    int recv_size = 0;
    if((recv_size=recv(clientfd,recv_buf,1024,0)) == -1){
        std::cout<<"recv rpc response failed"<<errno<<std::endl;
        close(clientfd);
        return;
    }
    //反序列化  在复制给response_str时,遇到\0就不复制了,
    // std::string response_str(recv_buf,0,recv_size);
    // if(!response->ParseFromString(response_str)){
    if(!response->ParseFromArray(recv_buf,recv_size)){
        std::cout<<"response parse error"<<errno<<std::endl;
        close(clientfd);
        return;
    }
    
    close(clientfd);
}

1、两边的协议

服务端(RpcProvider)解析逻辑[4字节header_size] + [rpc_header_str] + [args_str] 解析,且从 rpc_header_str 里的 RpcHeader 对象取 args_size 来读 args_str。

所以客户端也应该按照这个格式发送。

二、拼接、发送

cpp 复制代码
 //拼接发送的rpc请求字符串
    std::string send_rpc_str;
    send_rpc_str.insert(0,std::string((char *)&header_size,4));
    send_rpc_str += rpc_header_str;
    send_rpc_str += args_str;

这里拼接的都是序列化后的

cpp 复制代码
//发送rpc请求
    if(send(clientfd,send_rpc_str.c_str(),send_rpc_str.size(),0) == -1){
        std::cout<<"send rpc request failed"<<errno<<std::endl;
        return;
    }

这里使用.ctr()是因为

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  • buf 参数需要 const void*(或 const char*
  • C语言API不接受C++的 std::string 对象

三、建立连接

cpp 复制代码
//使用tcp编程完成rpc方法的远程调用
    int clientfd = socket(AF_INET,SOCK_STREAM,0);
    if(clientfd == -1){
        std::cout<<"create socket failed"<<errno<<std::endl;
        return;
    }

    //设置服务端的ip和port
    struct sockaddr_in server_addr;
    std::string ip = MprpcApplication::GetInstance().GetConfig().Load("rpcserverip");
    uint16_t port = atoi(MprpcApplication::GetInstance().GetConfig().Load("rpcserverport").c_str());
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip.c_str());

    //连接rpc服务节点
    int ret = connect(clientfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    if(ret == -1){
        std::cout<<"connect failed"<<errno<<std::endl;
        close(clientfd);
        exit(EXIT_FAILURE);
    }

先初始化好,然后用connect函数连接

四、接收响应

cpp 复制代码
//接收rpc响应
    char recv_buf[1024] = {0};
    int recv_size = 0;
    if((recv_size=recv(clientfd,recv_buf,1024,0)) == -1){
        std::cout<<"recv rpc response failed"<<errno<<std::endl;
        close(clientfd);
        return;
    }
    //反序列化  在复制给response_str时,遇到\0就不复制了,
    // std::string response_str(recv_buf,0,recv_size);
    // if(!response->ParseFromString(response_str)){
    if(!response->ParseFromArray(recv_buf,recv_size)){
        std::cout<<"response parse error"<<errno<<std::endl;
        close(clientfd);
        return;
    }

这里定义的recv_buf是数组,

通过注释里的方法,response_str只有\n,这是因为该构造函数希望以"\0"结尾

这样写也行

cpp 复制代码
//反序列化  在复制给response_str时,遇到\0就不复制了,
     std::string response_str(recv_buf,recv_size);
     if(!response->ParseFromString(response_str)){
    // if(!response->ParseFromArray(recv_buf,recv_size)){
        std::cout<<"response parse error"<<errno<<std::endl;
        close(clientfd);
        return;
    }

五、当前成果展示

相关推荐
gihigo19982 小时前
回溯搜索法与梯度下降/牛顿迭代结合的MATLAB优化实现
开发语言·matlab
fengfuyao9852 小时前
基于MATLAB的图像融合评价指标实现方案
开发语言·matlab
youngee112 小时前
hot100-49前缀树
开发语言·c#
海盗猫鸥2 小时前
「C++」继承
开发语言·c++
星月心城2 小时前
八股文-JavaScript(第二天)
开发语言·javascript·ecmascript
Dillon Dong2 小时前
从C到SIMULINK: 字节/字偏移 + 位偏移实现故障与故障字保存操作
c语言·开发语言·c#
m5655bj2 小时前
如何通过 C# 将 Markdown 转换为 PDF 文档
开发语言·pdf·c#
3824278272 小时前
python:yield用法
开发语言·python
wjs20242 小时前
WSDL 总结
开发语言