Rpc框架——服务端框架设计

news2024/9/27 17:36:55

目录

一、Network

二、Protocol

三、Dispatcher

四、RpcRouter 

五、Publish-Subscribe

六、Registry-Discovery 

七、Server 


服务端的功能需求:

基于网络通信接收客户端的请求,提供rpc服务

基于网络通信接收客户端的请求,提供服务注册与发现,上线&下线通知

基于网络通信接收客户端的请求,提供主题操作(创建/删除/订阅/取消),消息发布

在服务端的模块划分中,基于以上理解的功能,可以划分出这么几个模块:

  1. Network:网络通信模块

  2. Protocol:应用层通信协议模块

  3. Dispatcher:消息分发处理模块

  4. RpcRouter:远端调⽤路由功能模块

  5. Publish-Subscribe:发布订阅功能模块

  6. Registry-Discovery:服务注册/发现/上线/下线功能模块

  7. Server:基于以上模块整合而出的服务端模块

一、Network

该模块为网络通信模块,实现底层的网络通信功能,这个模块本质上也是⼀个比较复杂的模块,因此鉴于项⽬的庞大,该模块我们将使⽤陈硕大佬的Muduo库来进行搭建。

二、Protocol

应用层通信协议模块的存在意义:解析数据,解决通信中有可能存在的粘包问题,能够获取到⼀条完整的消息。

在muduo库基本使用中,我们能够知道想要让⼀个服务端/客⼾端对消息处理,就要设置⼀个onMessage的回调函数,在这个函数中对收到的数据进⾏应⽤层协议处理。

而Protocol模块就是对网络通信协议模块的设计,也就是在网络通信中,我们必须设计⼀个应用层的网络通信协议出来,以解决网络通信中可能存在的粘包问题,而解决粘包有三种方式:特殊字符间隔,定长,LV格式。

LV格式在处理不定长数据时,提供了更好的灵活性和效率,减少了解析的复杂性,在项目中,将使用LV格式来定义应用层的通信协议格式

Length:该字段固定4字节长度,⽤于表示后续的本条消息数据⻓度。

MyType:该字段为Value中的固定字段,固定4字节长度,用于表示该条消息的类型:

Rpc调用请求/响应类型消息

发布/订阅/取消订阅/消息推送类型消息

主题创建/删除类型消息

服务注册/发现/上线/下线类型消息

IDLength:为消息中的固定字段,该字段固定4字节长度,⽤于描述后续ID字段的实际长度。

MID:在每条消息中都会有⼀个固定字段为ID字段,用于唯⼀标识消息,ID字段长度不固定。

Body:消息主题正文数据字段,为请求或响应的实际内容字段。

三、Dispatcher

Dispathcher存在的意义:区分不同的消息类型,并根据消息类型调用不同的业务处理函数进行消息处理。

当muduo库底层通信收到数据后,在onMessage回调函数中对数据进⾏应用层协议解析,得到⼀条实际消息载荷后,我们就该决定这条消息代表这客户端的什么请求,以及应该如何处理。

因为Dispatcher需要根据不同的消息类型进行对应的函数处理,所以它应该存在一个哈希表hash_map<消息类型, 回调函数>,由使用者来决定哪条消息用哪个业务函数进行处理,当收到消息后,在该模块找到其对应的处理回调函数进行调用即可。

下面的Router、PS、RD模块都要向Dispatcher提供它们各自的回调函数。

四、RpcRouter 

RpcRouter存在的意义:提供rpc请求的处理回调函数(在Dispatcher中注册自己模块的回调函数),内部所要实现的功能,分辨出客户端请求的服务进行处理得到结果进行响应。

rpc请求中,最关键的两个点:

  • 请求方法名称

  • 请求对应要处理的参数信息

在Rpc远端调用中,首先将客户端到服务端的通信链路打通,然后将自己所需要调用的服务名称,以及参数信息传递给服务端,由服务端进行接收处理,并返回结果。

而不管是客户端要传递给服务端的服务名称以及参数信息,或者服务端返回的结果,都是在上边Protocol中定义的Body字段中,因此Body字段中就存在了另⼀层的正文序列化/反序列化过程。

序列化方式有很多种,鉴于当前我们是json-rpc,因此这个序列化过程我们就初步使⽤json序列化来进行,所定义格式如下:

//RPC-request
{
    "method" : "func", // 请求调用函数
    "parameters" : { // 调用函数传入参数
    "传参" : x
    }
}
//RPC-response
 // 处理成功
{
    "rcode" : OK,
    "result": y // 函数返回值
} 
 // 处理失败
{
    "rcode" : ERROR_INVALID_METHOD
}  

 

除此之外,因为RpcRouter模块要body中传入的Json::Value对象进行解析并拿取其中的方法名称与传入参数,那么为了保证服务端有对应的方法存在,这里还要额外设置一下服务描述,假如需要函数 int func(int num1, double num2) :

  • 服务名称:func

  • 参数名称:num1,int

  • 参数名称:num2,double

  • 返回值类型:int

有了这个描述,在回调函数中就可以先对传⼊的参数进行校验,没问题了则取出指定字段数据进行处理并返回结果

基于以上理解,在实现该模块时,该有以下设计:

  • 该模块必须具备⼀个Rpc路由管理,其中包含对于每个服务的参数校验功能

  • 该模块必须具备⼀个方法名称和方法业务回调的映射

  • 该模块必须向外提供Rpc请求的业务处理函数(提供给dispatcher模块)。

  • hash_map:RpcRouter内部的数据结构,可以进行相关校验,如果校验通过则可以调用对应的回调函数。

注意:RpcRouter的hash_map中的回调函数和Dispatcher中的回调函数不是一回事

Dispatcher是从网络服务器收到数据后的调度,有可能是发布订阅请求,有可能是rpc请求,根据不同的请求进行不同的分发处理

RpcRouter中的调度是针对收到的rpc请求的进一步调度,看要调用哪个rpc注册的回调函数进行rpc调用的处理

五、Publish-Subscribe

Publish-Subscribe存在的意义:针对发布订阅请求进⾏处理,提供⼀个回调函数设置给Dispatcher模块。

发布订阅所包含的请求操作:

  • 主题的创建

  • 主题消息的发布

  • 主题的删除

  • 主题的订阅/取消订阅

  1. 任意⼀个客户端在发布或订阅之前先创建⼀个主题,比如在新闻发布中我们创建⼀个音乐新闻主题,哪些客户端希望能够收到音乐新闻相关的消息,那么就订阅这个主题,服务端会建立起该主题与客户端之间的联系。

  2. 当某个客户端向服务端发布消息,且发布消息的目标主题是⾳乐新闻主题,则服务端会找出订阅了该主题的客户端,将消息推送给这些客户端。

从第2点可以看出来,推送消息涉及到客户端与服务端的交互,那么又是网络层的消息传输,所以又涉及到了序列化与反序列化,以下先定义出来格式:

//Topic-request
{
    //主题名称
    "key" : "music", 
    // 主题操作类型
    "optype" : TOPIC_CRAETE/TOPIC_REMOVE/TOPIC_SUBSCRIBE/TOPIC_CANCEL/TOPIC_PUBLISH,
    //TOPIC_PUBLISH请求才会包含有message字段
    "message" : "Hello World"
}

//Topic-response
 //successed
{
    "rcode" : OK,
} 
 //failed
{
"rcode" : ERROR_INVALID_PARAMETERS,
}  

功能思想并不复杂,因此我们需要把更多的精⼒放到其实现设计上:

  1. 主题管理——主题中需要保存订阅了该主题的客户端连接,

    1. 主题收到⼀条消息,需要将这条消息推送给订阅了该主题的所有客户端

    2. 数据结构上使用 hash_map<主题名称, topic>,其中topic中包含topic_nameset<connection>

  2. 订阅者管理——每个订阅者都需保存自己所订阅的主题名称

    1. 目的是为了当⼀个订阅客户端断开连接时,能够找到订阅信息的关联关系,进行删除

    2. 数据结构上使用hash_map<connection, subscriber>,其中subscriber中包含connectionvector<topc_name>

  3. 如何理解上下两个hash_map?

    1. 主题删除,先删除主题信息,然后删除该主题的订阅者,去这些订阅者中删除对应的主题名称信息

    2. 订阅者退出,删除订阅者信息,然后删除这个订阅者订阅的主题信息中包含的对应订阅者信息

  4. 该模块需要向外提供主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数

六、Registry-Discovery 

Registry-Discovery存在的意义:针对服务注册与发现请求的处理。

服务注册/发现类型请求中的详细划分

  • 服务注册:服务提供者告诉中转中心,自己能提供的服务

  • 服务发现:服务调用者询问中转中心,谁能提供指定服务

  • 服务上线:在⼀个提供者上线了指定服务后,通知发现过该服务的客户端有个provider可以提供该服务

  • 服务下线:在⼀个提供者断开连接,通知发现过该服务的caller,谁下线了哪个服务

服务注册模块,该模块主要是为了实现分布式架构存在,让每⼀个rpc客户端能够从不同的节点主机上获取自己所需的服务,让业务更具扩展性,系统更具健壮性。而为了能够让服务调用者知道有哪些服务提供者能提供自己所需服务,那么就需要有⼀个注册中心让这些服务提供者去注册登记自己的服务,让服务调用者来发现这些服务。

因此,在我们的服务端功能中,还需实现服务的注册/发现,以及服务的上线/下线功能。这也涉及到了网络层的消息传输,所以需要用到Json,下面来看一下定义的格式:

//Registry-Discovery request
{
    "optype" :
//provider进行服务注册/caller进行服务发现/在provider上下线后对caller进行服务上下线通知
    SERVICE_REGISTRY/SERVICE_DISCOVERY/SERVICE_ONLINE/SERVICE_OFFLINE,
    "method" : "func",
    
    //服务注册/上线/下线有host字段,发现则无host字段
    "host" : {
    "ip" : "127.0.0.1",
    "port" : 9090
    }
}

//Registry/Online/Offline-response
//成功
{
    "rcode" : OK,
}
//失败
{
    "rcode" : ERROR_INVALID_PARAMETERS,
}
//Discovery-response
{
    "method" : "func",
    "host" : [
    {"ip" : "127.0.0.1","port" : 9090},
    {"ip" : "127.0.0.2","port" : 8080}
    ]
}  

该模块的设计如下:

  1. 必须具备⼀个服务发现者的管理:

    1. 方法与发现者:当⼀个客户端端进行服务发现的时候,记录谁发现过该服务,当有⼀个新的提供者上线的时候,可以通知该发现者

    2. 连接与发现者:当⼀个发现者断开连接了,删除关联关系,往后就不需要通知了

  2. 必须具备⼀个服务提供者的管理:

    1. 连接与提供者:当⼀个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,该主机的该服务下线了

    2. 方法与提供者:能够知道谁的哪些方法下线了,然后通知发现过该方法的客户端

  3. 必须向Dispatcher模块提供⼀个服务注册/发现的业务处理回调函数

当⼀个rpc-provider登记了服务,则将其管理起来,当rpc-caller进行服务发现时,则将保存的对应服务所对应的主机信息,响应给rpc-caller。当中途⼀个rpc-provider上线登记服务时,则可以给进行了对应服务发现的rpc-caller进行服务上线通知,通知rpc-caller当前多了⼀个对应服务的rpc-provider。同时,当⼀个rpc-provider下线时,则可以找到进行了该服务发现的rpc-caller进行服务的下线通知。

七、Server 

当以上的所有功能模块都完成后,我们就可以将所有功能整合到⼀起来实现服务端程序了。

  • RpcServer:rpc功能模块与网络通信部分结合。

  • RegistryServer:服务发现注册功能模块与网络通信部分结合

  • TopicServer:发布订阅功能模块与网络通信部分结合。

RpcServer,只需要对内提供好rpc服务的路由关系管理,以及rpc请求消息的分发处理函数

针对接收到的请求根据路由关系,调用对应的回调函数进行参数检查,完成处理即可

在基于服务注册功能的RpcServer中需要包含有一个服务注册客户端实现向注册中心进行服务注册的功能

RegistryServer,需要对内做好服务提供者与服务发现者的数据管理,且提供服务操作请求的分发处理函数。这样,才能在收到服务注册请求后,将信息管理起来,收到服务发现请求后,向其返回可用的服务提供者信息。 

发布订阅服务器,对内做好主题信息与订阅者信息的管理,以便于能够在收到主题操作请求后完成其功能 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【POJ-1061 青蛙的约会】

题目 代码 #include <bits/stdc.h> using namespace std; typedef long long LL; LL ex_gcd(LL a, LL b, LL &x, LL &y) {if (b 0){x 1;y 0;return a;}LL gcd ex_gcd(b, a % b, x, y);LL tmp x;x y;y tmp - a / b * y;return gcd; } int main() {LL x, y…

springboot实战学习(9)(配置mybatis“驼峰命名“和“下划线命名“自动转换)(postman接口测试统一添加请求头)(获取用户详细信息接口)

接着学习。之前的博客的进度&#xff1a;完成用户模块的注册接口的开发以及注册时的参数合法性校验、也基本完成用户模块的登录接口的主逻辑的基础上、JWT令牌"的组成与使用以及完成了"登录认证"&#xff08;生成与验证JWT令牌&#xff09;具体往回看了解的链接…

SpringBoot项目编译运行成功,但有些包名类名仍然下划线标红的解决方法 | Idea

目录 问题解决方案&#xff1a;方法一&#xff1a;方法二【我用这个成功的】 问题 如图&#xff0c;成功运行但有些包名类名仍然下划线标红&#xff0c;强迫症抓狂 成功运行&#xff1a; 有些包导入标红&#xff1a; 解决方案&#xff1a; 方法一&#xff1a; 点击fil…

K8S介绍---搭建集群

Kubernetes介绍 官网&#xff1a;https://kubernetes.io/ 一、应用部署方式演变 1、传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其他技术的参与 缺点&#xff1a;不能为应用程序定义资源使用边界&a…

AXI4-Stream

AXI4-Stream 简介信号握手机制字节类型TKEEP和TSTRBAXI4-Stream Data FIFO正常模式packet模式 AXI4-Stream Interconnect 简介 这是一种连续流接口&#xff0c;不需要地址线&#xff08;很像 FIFO &#xff0c;一直读或一直写就行&#xff09;。对于这类 IP &#xff0c;ARM 不…

WebUI密码被锁定

锁定密码 打开-webui/打开-webui 讨论 #1027 (github.com) 当你忘记WebUI密码了

通信工程学习:什么是VPN虚拟专用网络

VPN:虚拟专用网络 VPN(Virtual Private Network),即虚拟专用网络,是一种通过公共网络(如互联网)建立私有网络连接的技术。以下是关于VPN的详细解释: 一、VPN虚拟专用网络的定义与原理 VPN通过公共网络(通常是互联网)建立一个临时的、安全的连接,形…

一维数组在内存中的存储

在之前的文章中&#xff0c;我已经介绍了一维数组的创建和初始化和一维数组的使用&#xff0c;今天我们来深入了解一下一维数组在内存中的存储形式。 首先我们先用代码打出数组元素的地址。 #include <stdio.h> int main() { int arr[8]{1,2,3,4,5,11,6,7,8}; int i0; …

单词记忆的化境:用思想的流水去淹没坚硬的石块

其实&#xff0c;鹅卵石通常都是很硬的。但是河底的石子&#xff0c;几乎大多都成了鹅卵石&#xff0c;它们被流水淹没&#xff0c;日复一日、夜以继日的冲刷着&#xff0c;没有了棱角。 在单词的记忆过程中&#xff0c;我们有太多的人&#xff0c;都有着不堪回首的往事&#x…

HDFS分布式文件系统01-HDFS JAVA操作与联邦机制

HDFS分布式文件系统 参考学习目标第三课时知识点1-HDFS的Java API介绍知识点2-案例-使用Java API操作HDFS 第四课时知识点1-Federation机制的实现原理知识点2-Federation机制的特点知识点3-Federation机制的实现知识点4-Erasure Coding 参考 maven的setting.xml配置文件详解 …

影刀RPA实战:网页爬虫之天猫商品数据

1.实战目标 1.1 实战目标 在电商行业&#xff0c;我们经常爬取各个平台的商品数据&#xff0c;通过收集和分析这些商品数据&#xff0c;企业可以了解市场趋势、消费者偏好和竞争对手的动态&#xff0c;从而制定更有效的市场策略。爬取商品数据对于企业在市场竞争中把握先机、…

招联金融2025校招内推喇

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 深圳&#xff0c;武汉&#xff1a; 后台开发 前端开发 数据开发 数据运营…

SD卡读写

SD卡 SD卡分类根据存储容量根据性能 SD卡协议简介SPI 模式命令命令格式命令类别CMDACMD 响应R1R2R3R7 寄存器CSD 总线读操作写操作擦除&写保护 初始化流程 SD 模式 IP 设计IP 例化界面IP 接口IP 状态机IP 验证 雷龙贴片式TF卡参考资料 SD卡分类 根据存储容量 Standard Ca…

五星级可视化页面(26):经常被模仿,从未被的超越的大屏界面。

Hello&#xff0c;各位老铁&#xff0c;本期分享的可视化界面&#xff0c;你可能在某些地方见过&#xff0c;或者被某些设计师临摹过&#xff0c;说明它们足够漂亮了&#xff0c; 你如果仔细观看细节&#xff0c;还是会发现很作出彩的地方不是轻易可以模仿的。 只有创新&#x…

linux网络编程8

24.9.25学习目录 一.原始套接字&#xff08;续&#xff09;1.sendto发送数据原始套接字1.ARP 二.Web编程1.概述2.HTML 一.原始套接字&#xff08;续&#xff09; 混杂模式&#xff1a; 指一台机器的网卡能够接受所有经过它的数据包&#xff0c;不论其目的地址是否是它&#xf…

【智能大数据分析 | 实验一】MapReduce实验:单词计数

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…

alpine安装docker踩坑记

文章目录 前言错误场景正确操作最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;最近使用alpine操作系统上docker遇到了一些错误&#xff0c;尝试解决之后就准备输出一篇博客&#xff0c;帮助有需要的后人能够少踩坑&#xff0c;因为淋过雨所以想给别人撑伞 错误场景 我…

dump java内存并进行分析

一、确保机器存在jmap命令 jmap 如果不存在&#xff0c;可以从其他机器将完整的jdk拷贝过来&#xff0c;然后进行使用 二、dump内存 ./jmap -dump:formatb,file./dump.hprof 2853 其中2853是java的PID&#xff0c;将其改为自己想要dump的java进程ID 三、分析&#xff0c;…

系统敏感信息搜索工具(支持Windows、Linux)

目录 工具介绍 使用说明 search模块 browser模块 下载地址 工具介绍 可以快速搜索服务器中的有关username,passsword,账号,口令的敏感信息还有浏览器的账户密码。 使用说明 search模块 searchall64.exe search -p 指定路径 searchall64.exe search -p 指定路径 -s &q…

前缀和(3)_寻找数组的中心下标

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 前缀和(3)_寻找数组的中心下标 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1…