[实现Rpc] 项目设计 | 服务端模块划分 | rpc | topic | server

目录

项目设计

1.理解项目功能

[2. 服务端 模块划分](#2. 服务端 模块划分)

[2.1 Network模块](#2.1 Network模块)

[2.2 Protocol模块](#2.2 Protocol模块)

[2.3 Dispatcher模块](#2.3 Dispatcher模块)

[2.4 RpcRouter模块](#2.4 RpcRouter模块)

[2.5 Publish-Subscribe模块](#2.5 Publish-Subscribe模块)

[2.6 Registry-Discovery模块](#2.6 Registry-Discovery模块)

[2.7 Server](#2.7 Server)


第三方库关于 future 的讲解部分,还没有整理完...之后整理出来了,再单独发叭,我们先来看一下对于项目的设计部分~


项目设计

1.理解项目功能

(1)

  • 本质上来讲,我们要实现的rpc(远端调用)思想上并不复杂,甚至可以说是简单
  • 其实就是客户端想要完成某个任务的处理,但是这个处理的过程并不自己来完成,而是将请求发送到服务器上,让服务器来帮其完成处理过程,并返回结果,客户端拿到结果后返回。
  • 然而上图的模型中,是⼀种 多对⼀或⼀对⼀的关系
  • ⼀旦服务端掉线,则客户端⽆法进行远端调用,且其服务端的负载也会较高
  • 因此在rpc实现中,我们不仅要实现其基本功能,还要再进⼀步,实现分布式架构的rpc。
  • 分布式架构: 简单理解就是 由多个节点组成的⼀个系统,这些节点通常指的是服务器,将不同的业务或者同⼀个业务 拆分分布在不同的节点上,通过协同⼯作解决高并发的问题,提高系统扩展性和可用性。

(2)

  • 其实现思想也并不复杂,也就是 在原来的模型基础上,增加⼀个注册中心,基于注册中心不同的服务提供服务器向注册中心进行服务注册
  • 相当于告诉注册中心自己能够提供什么服务,而客户端在进行远端调用前,先通过注册中心进行服务发现,找到能够提供服务的服务器,然后发起调用。

(3)

而其次的发布订阅功能,则是依托于 多个客户端围绕服务端进行消息的转发。

不过单纯的消息转发功能,并不能满足于大部分场景的需要,因此会在其基础上实现基于 主题订阅的转发。

(4)基于以上功能的合并,我们可以得到⼀个实现所有功能的结构图:

  • 在上图的结构中,我们甚至可以 让每⼀个Server作为备用注册中心形成分布式架构,⼀旦⼀个注册中心下线,可以向备用中心进行注册以及请求
  • 且在此基础上客⼾端在请求Rpc服务的时候,因为可以有多个rpc-provider可选,因此可以实现简单的负载均衡策略
  • 且基于注册中心可以更简便实现发布订阅的功能。

(5)项目的三个主要功能:

  • rpc调用。
  • 服务的注册/发现/下线/上线通知。
  • 主题消息的发布 订阅。

2. 服务端 模块划分

(1)服务端的功能需求:

  • 基于网络通信接收客户端的请求,提供 rpc服务。
  • 基于网络通信接收客户端的请求,提供 服务注册与发现,上线&下线通知。
  • 基于网络通信接收客户端的请求,提供 主题操作(创建/删除/订阅/取消),消息发布。

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

  1. Network:网络通信模块。
  2. Protocol:应用层通信协议模块。
  3. Dispatcher:消息分发处理模块。
  4. RpcRouter:远端调用路由功能模块。
  5. Publish-Subscribe:发布订阅功能模块。
  6. Registry-Discovery:服务注册/发现/上线/下线功能模块。
  7. Server:基于以上模块整合而出的服务端模块。

2.1 Network模块

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


2.2 Protocol模块

(1)

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

而Protocol模块就是是网络通信协议模块的设计,也就是在网络通信中,我们++必须设计⼀个应用层的网络通信协议出来,以解决网络通信中可能存在的粘包问题++

而解决粘包有三种方式:

  • 特殊字符间隔(如果数据有特殊字符就要另外处理)
  • 定长(有些数据大有些数据小,不好控制)
  • LV格式。

(2)本项目中将使用LV格式来定义应用层的通信协议格式:

  • Length:该字段固定4字节长度,用于表示后续的本条消息数据长度。
  • MType:该字段为Value中的固定字段,固定4字节长度,用于表示该条 消息的类型。
    • Rpc调用请求/响应类型消息。
    • 发布/订阅/取消订阅/消息推送类型消息。
    • 主题创建/删除类型消息。
    • 服务注册/发现/上线/下线类型消息。
  • IDLength:为消息中的固定字段,该字段固定4字节长度,用于描述后续ID字段的实际长度。
  • MID:在每条消息中都会有⼀个固定字段为ID字段,用于唯⼀标识消息,ID字段⻓度不固定。
  • Body:消息主题正⽂数据字段,为请求或响应的实际内容字段。

2.3 Dispatcher模块

(1)

模块存在的意义:

区分消息类型,根据不同的类型,调⽤不同的业务处理函数进行消息处理。

  • 当muduo库底层通信收到数据后,在onMessage回调函数中对数据进行应用层协议解析,得到⼀条实际消息载荷后

我们就该决定这条消息代表这客户端的什么请求,以及应该如何处理。

  • 因此,我们设计出了Dispatcher模块,作为⼀个分发模块,这个模块内部会保存有⼀个hash_map<消息类型,回调函数>
  • 以此由使用者来决定哪条消息 用哪个业务函数进行处理,当收到消息后,在该模块找到其对应的处理回调函数进行调用即可。

(2)消息类型:

  • rpc请求&响应。
  • 服务 注册/发现/上线/下线请求&响应。
  • 主题 创建/删除/订阅/取消订阅请求&响应,消息发布的请求&响应。

2.4 RpcRouter模块

(1)

RpcRouter模块存在的意义:

  • 提供rpc请求的 处理回调函数,内部所要实现的功能,分辨出客户端请求的服务进⾏处理得到结果进行响应。

(2)

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

  • 请求方法名称。
  • 请求对应要处理的参数信息。

在Rpc远端调用中,

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

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

(3)

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

//RPC-request
{
 	"method" : "Add",
 	"parameters" : 
 	{
 		"num1" : 11,
 		"num2" : 22
 	}
}
//RPC-response
{
 	"rcode" : OK,
 	"result": 33
}

{
 	"rcode" : ERROR_INVALID_PARAMETERS
}

需要注意的是

  • 在服务端,当接收到这么⼀条消息后,
  • Dispatcher模块会找到该 Rpc请求类型的回调处理函数进行业务处理,
  • 但是在进行业务处理的时候,也是只会将 parameters 参数字段传⼊回调函数中进行处理。

但是对服务端来说,应该从传⼊的Json::Value对象中,有什么样的参数,以及参数信息是否符合自己所提供的服务的要求,都应该有⼀个检测,是否符合要求,符合要求了再取出指定字段的数据进行处理。

(4)

因此,对服务端来说,在进行服务注册的时候,必须有⼀个服务描述,以代码段中的Add请求为例,该服务描述中就应该描述:

  • 服务名称: Add,
  • 参数名称: num1,是⼀个整形
  • 参数名称: num2,是⼀个整形,
  • 返回值类型:整形

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

(5)

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

  1. 该模块必须具备⼀个Rpc路由管理,其中包含对于每个服务的参数校验功能。
  2. 该模块必须具备⼀个方法名称和方法业务 回调的映射
  3. 该模块必须向外提供 Rpc请求的 业务处理函数。

2.5 Publish-Subscribe模块

(1)Publish-Subscribe模块存在的意义:

针对发布 主题订阅 请求进行处理,提供⼀个回调函数设置给Dispatcher模块。

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

  • 主题的创建
  • 主题的删除
  • 主题的订阅
  • 主题的取消订阅
  • 主题消息的发布

在当前的项目中,我们也实现⼀个简单的发布订阅功能,该功能是围绕 多个客户端与⼀个服务端来展开的。

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

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

(3)既然涉及到网络通信,那就先将通信消息的正文格式定义出来:

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

{
	"rcode" : ERROR_INVALID_PARAMETERS,
}

(4)功能思想并不复杂,所以将精力放到其实现设计上:

  • 该模块必须具备⼀个主题管理,且主题中需要保存订阅了该主题的客户端连接:
    • 主题收到⼀条消息,需要将这条消息推送给订阅了该主题的所有客户端
  • 该模块必须具备⼀个订阅者管理,且每个订阅者描述中都必须保存自己所订阅的主题名称:
    • 目的是为了当⼀个订阅客⼾端断开连接时,能够找到订阅信息的关联关系,进行删除
  • 该模块必须向外提供 主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数。

2.6 Registry-Discovery模块

(1)Registry-Discovery模块存在的意义:就是针对服务注册与发现请求的处理。

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

  • 服务注册:服务provider告诉中转中心,自己能提供哪些服务。
  • 服务发现:服务caller询问中转中心,谁能提供指定服务。
  • 服务上线:在⼀个provider上线了指定服务后,通知发现过该服务的客⼾端有个provider可以提供该服务。
  • 服务下线:在⼀个provider断开连接,通知发现过该服务的caller,谁下线了哪个服务。

服务注册模块,该模块主要是为了实现分布式架构而存在,让每⼀个rpc客户端能够从不同的节点主机上获取⾃⼰所需的服务,让业务更具扩展性,系统更具健壮性。

  • ⽽为了能够让rpc-caller知道有哪些rpc-provider能提供⾃⼰所需服务,那么就需要有⼀个注册中⼼让这些rpc-provider去注册登记⾃⼰的服务,让rpc-caller来发现这些服务。

(3)因此,在我们的服务端功能中,还需实现服务的注册/发现,以及服务的上线/下线功能。

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

//Registry/Online/Offline-response
{
 "rcode" : OK,
}

//error-response
{
 "rcode" : ERROR_INVALID_PARAMETERS,
}

//Discovery-response
{
 	"method" : "Add",
 	"host" : 
 	[
 		{"ip" : "127.0.0.1","port" : 9090},
 		{"ip" : "127.0.0.2","port" : 8080}
 	]
}

(4)该模块的设计如下:

  • 必须具备⼀个服务发现者的管理:
    • 方法与发现者:当⼀个客⼾端进行服务发现的时候,进行记录谁发现过该服务,当有⼀个新的提供者上线的时候,可以通知该发现者。
    • 连接与发现者:当⼀个发现者断开连接了,删除关联关系,往后就不需要通知了。
  • 必须具备⼀个服务提供者的管理:
    • 连接与提供者:当⼀个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,该主机的该服务下线了。
    • 方法与提供者:能够知道谁的哪些方法下线了,然后通知发现过该方法的客户端。
  • 必须向Dispatcher模块提供⼀个服务注册/发现的业务处理回调函数。

这样,当⼀个rpc-provider登记了服务,则将其管理起来,当rpc-caller进行服务发现时,则将保存的对应服务所对应的主机信息,响应给rpc-caller。

  • 当中途⼀个rpc-provider上线登记服务时,则可以给进行了对应服务发现的rpc-caller进行服务上线通知,通知rpc-caller当前多了⼀个对应服务的rpc-provider。

(5)同时,当⼀个rpc-provider下线时,则可以找到进行了该服务发现的rpc-caller进行服务的下线通知。

2.7 Server

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

  • RpcServer:rpc功能模块与网络通信部分结合。
  • TopicServer:主题 发布订阅功能模块与网络通信部分结合。
  • RegistryServer:服务发现注册功能模块与⽹络通信部分结合

(2)RpcServer结构图:

(3)RegistryServer结构图:

(4)TopicServer结构图:

相关推荐
USER_A00121 分钟前
JavaScript笔记进阶篇01——作用域、箭头函数、解构赋值
javascript·笔记
我想学LINUX36 分钟前
【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScript&Java & Python&C/C++)
java·c语言·javascript·网络·python·5g·华为od
新知图书39 分钟前
Linux C\C++编程-文件位置指针与读写文件数据块
linux·c语言·c++
qystca1 小时前
异或和之和
数据结构·c++·算法·蓝桥杯
不一样的信息安全1 小时前
Spring Boot框架下的上海特产销售商城网站开发之旅
网络·spring boot
hgdlip1 小时前
IP属地:是身份证还是手机归属地?
网络·tcp/ip·智能手机
涛ing2 小时前
19. C语言 共用体(Union)详解
java·linux·c语言·c++·vscode·算法·visual studio
次元工程师!2 小时前
JAVA-IO模型的理解(BIO、NIO)
java·笔记·学习·nio·bio·io模型
wxjlkh2 小时前
VMware虚拟机迁移到阿里云
服务器·网络
周杰伦_Jay2 小时前
Ollama能本地部署Llama 3等大模型的原因解析(ollama核心架构、技术特性、实际应用)
数据结构·人工智能·深度学习·架构·transformer·llama