RpcProvider(rpc服务提供者)实现思路
RpcProvider(服务提供者)实现思路
上一节说到,如何将一个本地服务发布成远程服务,但没有说明一个rpc框架怎么进行调用的,看看上节代码
#include <iostream>
#include <string>
#include "user.pb.h"
class UserService : public fixbug::UserServiceRpc //使用rpc服务发布端(rpc服务提供者)
{
public:bool Login(std::string name, std::string pwd){std::cout<<"doing local service : Login" << std::endl;std::cout<< "name:" << name << "pwd :" << pwd << std::endl;} /*** 重写基类UserServiceRpc的虚函数 下面这些方法都是框架直接调用的* caller ===> Login(LoginRequest) ==> transmit ==> callee* callee ===> Login(LoginRequest) ===> 调用下述的Login方法* */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->pwd();//本地业务bool res = Login(name, pwd);//把响应写入 包括错误码、错误消息、返回值response->set_success(0);fixbug::ResultCode* rc = response->mutable_result();rc->set_errcode(0);rc->set_errmsg("");//执行回调操作 执行响应对象数据的序列化和网络发送(由框架来完成)done->Run();}
};
现在实现的发布服务方,那么显然Login方法是rpc框架帮我们调用的,什么时候调用?当rpc客户端通过网络发送rpc调用请求之后,这边接收到rpc请求,解析请求然后调用发布的服务方法,获取服务方法的响应在回传给rpc客户端即完成了一次远程rpc调用过程。
下面一一分析上面步骤的实现思路。
1、 首先,RpcProvider肯定是一个服务器,接收来自rpc客户端的请求,且能在一定程度上承载高并发的需求(考虑多个rpcClient给当前rpcProvider发送rpc调用请求)。显而易见RpcProvider需要一个高性能的网络库来做这些任务,那么可以考虑muduo网络库。
2、 网络库有了,用什么消息格式?–protobuf(优势就不赘述了)。 消息包含哪些内容?一个rpcclient发送请求过来调用一个远程方法,那么rpcProvider收到这个请求之后,能根据请求所携带的数据自动调用发布的rpc方法,那么请求必须包含服务名、方法名、以及参数,这样rpcProvider才知道怎么调用。即buffer = service_name + method_name + args。
由于使用的是protobuf二进制消息存储,所以涉及到读取这几个参数的问题,可以将buffer加个4字节(int不会超过4字节)的head_size,head_size表示service_name和method_name的长度,这样可以先读取head_size,在根据head_size读取service_name+method_name。
最后读取args,思考一个问题,剩下的数据是不是都是args?不一定,考虑Tcp粘包的情况,一次接收到2个rpc调用请求,那么args也需要一个长度args_size来表示当前解析的rpc调用的参数长度。
因此这里借助protobuf定义一个消息格式即
message RpcHeader{bytes service_name = 1;bytes method_name = 2;uint32 args_size = 3;
}
那么header_size表示是该消息的长度,先读header_size,在读RpcHeader所包含的字节流,最后借助protobuf反序列化出这三个参数,那么得到了service_name 、method_name 、args_size。在接着读取args_size长度的字符流数据得到args。最后通过protobuf定义的参数格式反序列化出request。最终所需要的参数都已经拿到了。
3、 那么如何通过上面得到的参数自动调用rpc服务?首先得在本地存在这个远程服务,前面提到rpc框架来调用Login方法,那么 rpcProvider得预先对发布的 rpc方法做一下映射 。
struct ServiceInfo{google::protobuf::Service* m_service; //保存服务对象std::unordered_map<std::string, const google::protobuf::MethodDescriptor*> m_methodMap; //保存服务的所有方法的映射关系};//存储注册成功的服务对象和其服务方法std::unordered_map<std::string, ServiceInfo> m_serviceMap; // service_name ---> ServiceInfo
例如UserService存在Login 和 Register两个方法, 由于UserService继承自UserServiceRpc ,那么可以借助protobuf来实现这预先实现这个映射。具体如下:
//框架提供给外部使用的,可以发布rpc方法的函数接口
void RpcProvider::NotifyService(google::protobuf::Service* service)
{ServiceInfo service_info;//获取服务对象的描述信息const google::protobuf::ServiceDescriptor *pserviceDesc = service->GetDescriptor();//获取服务的名字std::string service_name = pserviceDesc->name();//获取服务对象service的方法的数量int methodCnt = pserviceDesc->method_count();std::cout<< " service_name " << service_name << std::endl;for(int i = 0; i<methodCnt; ++i){//获取了服务对象指定下标的服务方法的描述(抽象描述) UserService Loginconst google::protobuf::MethodDescriptor *pmethodDesc = pserviceDesc->method(i);std::string method_name = pmethodDesc->name();service_info.m_methodMap.insert({method_name, pmethodDesc});std::cout<<" method_name: " << method_name << std::endl; }service_info.m_service = service;m_serviceMap.insert({service_name, service_info});}
RpcProvider可以借助这个方法注册一个rpc服务的站点,这样由RpcClient发送过来的rpc调用请求,通过muduo和protobuf解析得到上述几个参数之后,即可通过映射表调用指定的rpc服务。
即:
1、server_name, method_name, args= parse(buffer); //解析参数
it = m_serviceMap.find(service_name);
method = it->second.m_methodMap.find(method_name);
request = service->GetRequestPrototype(method).New();
2、request->ParseFromString(args_str) //序列化请求参数
response = service->GetResponsePrototype(method).New();
3、service->CallMethod(method, nullptr ,request, response, done); //调用具体的rpc方法
---->
4、最终调用:LoginLogin(::google::protobuf::RpcController* controller,const ::fixbug::LoginRequest* request,::fixbug::LoginResponse* response,::google::protobuf::Closure* done)
--->
5、回调 done()
最后服务器调用完指定rpc方法后(此时response已填入响应),那么需要将respnse借助protobuf反序列化字节流数据,再通过muduo网络库发回给rpcClient,这就是done回调函数所做的事情。
即:
void done(fd, response)
{string response_str;if (response->SerializeToString(&response_str)) //将响应进行序列化{//序列化成功后,通过网络把rpc方法执行的结果发送给rpc的调用方fd->send(response_str);}fd->shutdown(); //模拟http的短链接服务, 由rpcprovider主动断开连接以免占用资源
}
到此RpcProvider基本实现。
相关文章:
RpcProvider(rpc服务提供者)实现思路
RpcProvider(服务提供者)实现思路 上一节说到,如何将一个本地服务发布成远程服务,但没有说明一个rpc框架怎么进行调用的,看看上节代码 #include <iostream> #include <string> #include "user.pb.h…...
GNSS技术知识你知道多少?这些你或许还未掌握
GNSS信号频段 GNSS频谱图展示了不同的GNSS信号及其星座、载波频率、调制方案,以及所有这些信号在同一L波段频段内如何相互关联,是GNSS专业人员的必备工具,包括设计和开发GNSS系统的工程师,以及测试GNSS系统的工程师。 GNSS术语 …...
YOLOv8教程系列:三、使用YOLOv8模型进行自定义数据集半自动标注
YOLOv8半自动标注 目标检测半自动标注的优点包括: 1.提高标注效率:算法能够自动标注部分数据,减少了人工标注的工作量,节省时间和资源。 2.降低成本:自动标注可以减少人工标注的成本,特别是对于大规模数据…...
AI聊天GPT三步上篮!
1、是什么? CHATGPT是OpenAI开发的基于GPT(Generative Pre-trained Transformer)架构的聊天型人工智能模型。也就是你问它答,根据网络抓去训练 2、怎么用? 清晰表达自己诉求,因为它就是一个AI助手&#…...
如何彻底卸载VMware
目录 第一章、停止并卸载VMware程序1.1)停止VMware有关的服务1.2)打开任务管理器停止进程1.3)卸载VMware程序 第二章、残留文件删除2.1)打开注册表2.2)删除注册表残留文件2.3)C盘文件删除 友情提醒…...
[个人笔记] Windows配置NTP时间同步
Windows - 运维篇 第六章 Windows配置NTP时间同步 Windows - 运维篇系列文章回顾Windows配置NTP时间同步域控环境的NTP配置工作组环境的NTP配置Windows的CMD部分命令集 参考来源 系列文章回顾 第一章 迁移WinSrv系统到虚拟机 第二章 本地安全策略xcopy实现实时备份文件夹内容 …...
Jetson Docker 编译 FFmpeg 支持硬解nvmpi和cuvid
0 设备和docker信息 设备为NVIDIA Jetson Xavier NX,jetpack版本为 5.1.1 [L4T 35.3.1] 使用的docker镜像为nvcr.io/nvidia/l4t-ml:r35.2.1-py3,详见https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-ml 使用下列命令拉取镜像: sudo docker pull nvcr…...
某某某小说app接口抓包分析
详细说明查看原文 https://sdk.qzbonline.com/ver9/shuhuajs/sdk/ioszh_shuhuajs_conf.htmlhttps://sdk.qzbonline.com/prov8/ymqxs/sdk/ios_ymqxs_conf.htmlhttps://sdk.qzbonline.com/prov8/ymqxs/sdk/ios_ymqxs_conf2.htmlhttps://sdk.qzbonline.com/prov8/fqhyxs/sdk/iosz…...
开发一个RISC-V上的操作系统(四)—— 内存管理
目录 往期文章传送门 一、内存管理简介 二、Linker Script 链接脚本 三、动态分配内存 四、测试 往期文章传送门 开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统(二…...
区块链:可验证随机函数
本篇主要介绍可验证随机函数的定义及其在区块链上的作用。 1 可验证随机函数 1.1 定义 可验证随机函数(Verifiable Random Function,VRF)本质上还是一类具有验证功能的伪随机函数。对于一个特定的输入 m m m以及输入者的私钥 S K SK SK,VRF会输出一个随…...
Flask中flask-session
Flask中flask-session Flask-Session是一个为Flask应用程序开发的工具,允许您轻松处理服务器端会话。会话是存储和追踪用户特定数据的方式。例如,当用户登录到应用程序时,他们的状态(即登录状态)可以保存在会话中&…...
react-Native init初始化项目报错”TypeError: cli.init is not a function“
文章目录 一、问题:二、解决: 一、问题: 在react-native init appDemo 创建项目时,报错TypeError: cli.init is not a function。 二、解决: 产生这个问题的原因是:使用这种方式创建工程,rea…...
【gitlib】linux系统rpm安装gitlib最新版本
目录 下载gitlib安装包 安装需要的依赖 设置开机启动 安装邮件服务器并设置开机启动 rpm执行安装gitlib 修改gitlib.rb文件的属性 修改完毕后执行更新配置 查看gitlib运行 查看gitlib初始化root密码 gitlib入口访问地址 下载gitlib安装包 Index of /gitlab-ce/yum/el7/…...
iOS开发-检查版本更新与强制更新控制
iOS开发-检查版本更新与强制更新控制。 在开发中经常遇到需要检查版本,检查版本及请求appstoreLookUrl查看版本号与当前的版本号进行比对,看是否需要更新。强制更新控制,是将获取到当前版本号传给服务端,服务端判断当前的版本是否…...
自动化运维工具——Ansible
自动化运维工具——Ansible 一、Ansible概述二、ansible 环境安装部署1.管理端安装 ansible2.ansible 目录结构3.配置主机清单4.配置密钥对验证 三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9&a…...
W2NER详解
论文:https://arxiv.org/pdf/2112.10070.pdf 代码:https://github.com/ljynlp/W2NER 文章目录 W2NER介绍模型架构解码 源码介绍数据输入格式模型代码 参考资料 W2NER 介绍 W2NER模型,将NER任务转化预测word-word(备注ÿ…...
ElementUI tabs标签页样式改造美化
今天针对ElementUI的Tabs标签页进行了样式修改,更改为如下图所属的样子。 在线运行地址:JSRUN项目-ElementUI tabs标签页样式改造 大家如果有需要可以拿来修改使用,下面我也简单的贴上代码,代码没有注释,很抱歉&#x…...
出海周报|Temu在美状告shein、ChatGPT安卓版上线、小红书回应闪退
工程机械产业“出海”成绩喜人,山东相关企业全国最多Temu在美状告shein,跨境电商战事升级TikTok将在美国推出电子商务计划,售卖中国商品高德即将上线国际图服务,初期即可覆盖全球超200个国家和地区ChatGPT安卓版正式上线ÿ…...
2023年7月26日 单例模式
单例模式 饿汉模式 package com.wz.cinema.platform.server.util;public class DataManager {/*** 单例模式:整个类在运行中只会有一个实例* 既然是在运行中只有一个实例,那么就必须* 考虑多线程环境** 单例模式分为懒汉模式和饿汉模式* 饿汉模式本身就是…...
[ 容器 ] Docker 安全及日志管理
目录 Docker 容器与虚拟机的区别Docker 存在的安全问题Docker 架构缺陷与安全机制Docker 安全基线标准容器相关的常用安全配置方法限制流量流向镜像安全避免Docker 容器中信息泄露DockerClient 端与 DockerDaemon 的通信安全 容器的安全性问题的根源在于容器和宿主机共享内核。…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
