当前位置: 首页 > news >正文

C++ - 从零实现Json-Rpc框架-2(服务端模块 客户端模块 框架设计)

 项⽬设计

本质上来讲,我们要实现的rpc(远端调⽤)思想上并不复杂,甚⾄可以说是简单,其实就是客⼾端想要完成某个任务的处理,但是这个处理的过程并不⾃⼰来完成,⽽是,将请求发送到服务器上,让服务器来帮其完成处理过程,并返回结果,客⼾端拿到结果后返回。

然⽽上图的模型中,是⼀种多对⼀或⼀对⼀的关系,⼀旦服务端掉线,则客⼾端⽆法进⾏远端调⽤,且其服务端的负载也会较⾼,因此在rpc实现中,我们不仅要实现其基本功能,还要再进⼀步,实现分布式架构的rpc。
分布式架构:简单理解就是由多个节点组成的⼀个系统,这些节点通常指的是服务器,将不同的业务或者同⼀个业务拆分分布在不同的节点上,通过协同⼯作解决⾼并发的问题,提⾼系统扩展性和可⽤性。
其实现思想也并不复杂,也就是在原来的模型基础上,增加⼀个注册中⼼,基于注册中⼼不同的服务提供服务器向注册中⼼进⾏服务注册,相当于告诉注册中⼼⾃⼰能够提供什么服务,⽽客⼾端在进⾏远端调⽤前,先通过注册中⼼进⾏服务发现,找到能够提供服务的服务器,然后发起调⽤。

⽽其次的发布订阅功能,则是依托于多个客⼾端围绕服务端进⾏消息的转发。
不过单纯的消息转发功能,并不能满⾜于⼤部分场景的需要,因此会在其基础上实现基于主题订阅的转发。

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

在上图的结构中,我们甚⾄可以让每⼀个Server作为备⽤注册中⼼形成分布式架构,⼀旦⼀个注册中⼼下线,可以向备⽤中⼼进⾏注册以及请求,且在此基础上客⼾端在请求Rpc服务的时候,因为可以有多个rpc-provider可选,因此可以实现简单的负载均衡策略,且基于注册中⼼可以更简便实现发布订阅的功能。
项⽬的三个主要功能:
• rpc调⽤
• 服务的注册与发现以及服务的下线/上线通知
• 消息的发布订阅

服务端模块划分

服务端的功能需求:
• 基于⽹络通信接收客⼾端的请求,提供rpc服务
• 基于⽹络通信接收客⼾端的请求,提供服务注册与发现,上线&下线通知
• 基于⽹络通信接收客⼾端的请求,提供主题操作(创建/删除/订阅/取消),消息发布
在服务端的模块划分中,基于以上理解的功能,可以划分出这么⼏个模块

模块名称功能描述
Network负责网络通信(TCP/HTTP),接收和发送消息
Protocol解析和封装 RPC 请求/响应协议(JSON、Protobuf 等)
Dispatcher消息调度模块,将不同的请求分发到正确的处理函数
RpcRouter远程调用路由,确定请求应该发送到哪个服务器
Publish-Subscribe发布-订阅模块,支持主题创建、订阅、消息推送
Registry-Discovery服务注册与发现,管理可用的 RPC 服务
Server核心服务端,整合所有模块并提供外部接口

1.Network网络通信

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

2.Protocol解析和封装报文

应⽤层通信协议模块的存在意义:解析数据,解决通信中有可能存在的粘包问题,能够获取到⼀条完整的消息。
在前边的muduo库基本使⽤中,我们能够知道想要让⼀个服务端/客⼾端对消息处理,就要设置⼀个onMessage的回调函数,在这个函数中对收到的数据进⾏应⽤层协议处理。
⽽Protocol模块就是是⽹络通信协议模块的设计,也就是在⽹络通信中,我们必须设计⼀个应⽤层的⽹络通信协议出来,以解决⽹络通信中可能存在的粘包问题,⽽解决粘包有三种⽅式:特殊字符间隔,定⻓,LV格式。(一个定长字段表示后面变长数据的大小)
⽽我们项⽬中将使⽤LV格式来定义应⽤层的通信协议格式

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

3.Dispatcher消息派发

模块存在的意义:区分消息类型,根据不同的类型,调⽤不同的业务处理函数进⾏消息处理。
当muduo库底层通信收到数据后,在onMessage回调函数中对数据进⾏应⽤层协议解析,得到⼀条实际消息载荷后,我们就该决定这条消息代表这客⼾端的什么请求,以及应该如何处理。
因此,我们设计出了Dispatcher模块,作为⼀个分发模块,这个模块内部会保存有⼀个hash_map<消息类型, 回调函数>,以此由使⽤者来决定哪条消息⽤哪个业务函数进⾏处理,当收到消息后,在该模块找到其对应的处理回调函数进⾏调⽤即可。

1.Protocol(协议解析):

作用

  • onMessage 处理收到的原始数据,解析成完整消息
  • Protocol 负责拆包,解析 LV 格式,确保数据完整
  • 解析后的数据被传递给 onMessageCallback 进行后续处理

流程

  1. Muduo 网络层 收到数据后,触发 onMessage 回调。
  2. onMessage 调用 Protocol 解析数据,并将完整消息传递给 onMessageCallback
  3. onMessageCallback 进一步解析消息类型,并选择合适的处理方式

2.Dispatcher(消息分发器)

作用

  • 基于消息类型 (MType) 查找对应的业务处理函数
  • 维护 map<消息类型, 处理回调>
  • 注册业务逻辑(registerHandler(),确保不同类型的消息能正确处理

流程

  1. onMessage 解析后,提取 MType(消息类型)。
  2. 通过 Dispatchermap<MType, 回调函数> 找到对应的业务处理函数
  3. 调用 注册的回调函数 处理具体的业务逻辑。

3.Muduo 网络模块

作用

  • 负责 TCP 连接管理onConnectionCallback
  • 负责 数据接收onMessage
  • 自动管理缓冲区,防止数据丢失

流程

  1. 当有新的客户端连接,Muduo 触发 onConnectionCallback 进行管理。
  2. 客户端发送数据后,Muduo 触发 onMessage 处理数据。
  3. 解析后的消息被传递给 Dispatcher 进行业务处理。

整体框架:
 

Muduo::TcpServer / TcpClient  (管理连接 & 数据收发)│▼收到数据 -> 触发 `onMessage`│▼`Protocol` 解析完整消息 (解决粘包问题)│▼`Dispatcher` 分发业务逻辑│├── RPC 处理├── 服务注册 & 发现├── 发布订阅└── 其他业务逻辑

关系总结

模块作用依赖
Muduo网络通信,负责 TCP 连接、数据收发onMessage 触发 Protocol 解析
Protocol解析数据流,解决粘包问题,提取完整消息解析数据后调用 Dispatcher
Dispatcher基于消息类型 MType 分发业务逻辑依赖 Protocol 解析的 MType, Body
业务逻辑最终执行具体的 RPC、注册、订阅等业务通过 registerHandler() 注册到 Dispatcher
Muduo 负责网络通信,触发 onMessage
Protocol 负责协议解析,保证完整消息
Dispatcher 负责分发业务逻辑,调用相应处理函数

4. RpcRouter RPC 路由器(处理 RPC 类型的消息)

RpcRouter模块存在的意义:提供rpc请求的处理回调函数,内部所要实现的功能,分辨出客⼾端请求的服务进⾏处理得到结果进⾏响应。

pc请求中,最关键的两个点:
• 请求⽅法名称
• 请求对应要处理的参数信息
在Rpc远端调⽤中,⾸先将客⼾端到服务端的通信链路打通,然后将⾃⼰所需要调⽤的服务名称,以及参数信息传递给服务端,由服务端进⾏接收处理,并返回结果
⽽,不管是客⼾端要传递给服务端的服务名称以及参数信息,或者服务端返回的结果,都是在上边Protocol中定义的Body字段中,因此Body字段中就存在了另⼀层的正⽂序列化/反序列化过程。
序列化⽅式有很多种,鉴于当前我们是json-rpc,因此这个序列化过程我们就初步使⽤json序列化来进⾏.

需要注意的是,在服务端,当接收到这么⼀条消息后,Dispatcher模块会找到该Rpc请求类型的回调处理函数进⾏业务处理,但是在进⾏业务处理的时候,也是只会将 parameters 参数字段传⼊回调函数中进⾏处理。
然⽽,对服务端来说,应该从传⼊的Json::Value对象中,有什么样的参数,以及参数信息是否符合⾃⼰所提供的服务的要求,都应该有⼀个检测,是否符合要求,符合要求了再取出指定字段的数据进⾏处理。
因此,对服务端来说,在进⾏服务注册的时候,必须有⼀个服务描述,以代码段中的Add请求为例,该服务描述中就应该描述:
◦ 服务名称: Add,
◦ 参数名称: num1,是⼀个整形
◦ 参数名称: num2,是⼀个整形,
◦ 返回值类型:整形
有了这个描述,在回调函数中就可以先对传⼊的参数进⾏校验,没问题了则取出指定字段数据进⾏处理并返回结果
基于以上理解,在实现该模块时,该有以下设计:
1. 该模块必须具备⼀个Rpc路由管理,其中包含对于每个服务的参数校验功能
2. 该模块必须具备⼀个⽅法名称和⽅法业务回调的映射
3. 该模块必须向外提供 Rpc请求处理接口

模块作用
ServiceDescribe维护 方法名、参数格式、回调函数
RpcRouter维护方法名称与 ServiceDescribe 的映射,提供 RPC 处理能力
Dispatcher分发 RPC 请求onRpcRequest
onRpcRequest接收 Dispatcher 分发的请求,调用 RpcRouter 处理

5.Publish-Subscribe 发布订阅(处理发布订阅请求)

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

发布订阅所包含的请求操作:
• 主题的创建
• 主题的删除
• 主题的订阅
• 主题的取消订阅
• 主题消息的发布
在当前的项⽬中,我们也实现⼀个简单的发布订阅功能,该功能是围绕多个客⼾端与⼀个服务端来展开的。
即,任意⼀个客⼾端在发布或订阅之前先创建⼀个主题,⽐如在新闻发布中我们创建⼀个⾳乐新闻主题,哪些客⼾端希望能够收到⾳乐新闻相关的消息,则就订阅这个主题,服务端会建⽴起该主题与客⼾端之间的联系。
当某个客⼾端向服务端发布消息,且发布消息的⽬标主题是⾳乐新闻主题,则服务端会找出订阅了该主题的客⼾端,将消息推送给这些客⼾端。

功能思想并不复杂,因此我们需要把更多的精⼒放到其实现设计上:
1. 该模块必须具备⼀个主题管理,且主题中需要保存订阅了该主题的客⼾端连接
        a. 主题收到⼀条消息,需要将这条消息推送给订阅了该主题的所有客⼾端
2. 该模块必须具备⼀个订阅者管理,且每个订阅者描述中都必须保存⾃⼰所订阅的主题名称
        a. ⽬的是为了当⼀个订阅客⼾端断开连接时,能够找到订阅信息的关联关系,进⾏删除
3. 该模块必须向外提供 主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数

模块作用依赖
Dispatcher基于 MType 分发 订阅/发布 请求PubSubManager
PubSubManager管理 Topic & Subscriber,执行 订阅/发布Topic, Subscriber
Topic维护订阅者列表,处理 publish()Subscriber
Subscriber记录已订阅的主题,用于取消订阅Topic
Connection代表客户端,标识某个 SubscriberSubscriber

✅ Dispatcher 负责解析 & 分发

✅ PubSubManager 负责 订阅 & 主题 逻辑

✅ Topic 维护 订阅者列表,支持 消息推送

✅ Subscriber 维护 已订阅主题,支持 取消订阅

6.Registry-Discovery服务注册与发现请求的处理

Registry-Discovery模块存在的意义:就是针对服务注册与发现请求的处理。
• 服务注册/发现类型请求中的详细划分
◦ 服务注册:服务provider告诉中转中⼼,⾃⼰能提供哪些服务
◦ 服务发现:服务caller询问中转中⼼,谁能提供指定服务
◦ 服务上线:在⼀个provider上线了指定服务后,通知发现过该服务的客⼾端有个provider可以提供该服务
◦ 服务下线:在⼀个provider断开连接,通知发现过该服务的caller,谁下线了哪个服务
服务注册模块,该模块主要是为了实现分布式架构⽽存在,让每⼀个rpc客⼾端能够从不同的节点主机上获取⾃⼰所需的服务,让业务更具扩展性,系统更具健壮性。
⽽为了能够让rpc-caller知道有哪些rpc-provider能提供⾃⼰所需服务,那么就需要有⼀个注册中⼼让这些rpc-provider去注册登记⾃⼰的服务,让rpc-caller来发现这些服务。
因此,在我们的服务端功能中,还需实现服务的注册/发现,以及服务的上线/下线功能。

该模块的设计如下:
1. 必须具备⼀个服务发现者的管理:
        a. ⽅法与发现者:当⼀个客⼾端进⾏服务发现的时候,进⾏记录谁发现过该服务,当有⼀个新的提供者上线的时候,可以通知该发现者
        b. 连接与发现者 :当⼀个发现者断开连接了,删除关联关系,往后就不需要通知了
2. 必须具备⼀个服务提供者的管理:
        a. 连接与提供者:当⼀个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,该主机的该服务下线了.(提供者的所有方法删除通知对应的发现者
        b. ⽅法与提供者:能够知道谁的哪些⽅法下线了,然后通知发现过该⽅法的客⼾端(提供者的一部分方法删除通知对应的发现者
3. 必须向Dispatcher模块提供⼀个服务注册/发现的业务处理回调函数这样,当⼀个rpc-provider登记了服务,则将其管理起来,当rpc-caller进⾏服务发现时,则将保存的对应服务所对应的主机信息,响应给rpc-caller。

组件作用
Dispatcher解析 RPC MType,分发服务请求
RDManager维护 Provider & Caller,提供 onServiceRequest()
Provider记录 提供的 method,管理 host:port
Discoverer记录 发现的 method,支持 通知 Caller
✅ Dispatcher 负责解析 & 分发
✅ RDManager 负责 服务注册 & 发现
✅ Provider 维护 服务信息,支持 上线/下线
✅ Discoverer 维护 发现信息,支持 Caller 通知

7.Server

当以上的所有功能模块都完成后,我们就可以将所有功能整合到⼀起来实现服务端程序了。
• RpcServer:rpc功能模块与⽹络通信部分结合。
• RegistryServer:服务发现注册功能模块与⽹络通信部分结合
• TopicServer:发布订阅功能模块与⽹络通信部分结合。

模块作用核心组件
RpcServer提供 RPC 服务,并进行远程调用TcpServerRpcRouterDispatcher
RegistryServer管理服务注册/发现,通知上线/下线RegDisManagerRegistryDiscovery
TopicServer实现发布-订阅机制,支持消息推送PubSubManagerTopicSubscriber

客⼾端模块划分

在客⼾端的模块划分中,基于以上理解的功能,可以划分出这么⼏个模块
1. Protocol:应⽤层通信协议模块
2. Network:⽹络通信模块
3. Dispatcher:消息分发处理模块
4. Requestor:请求管理模块
5. RpcCaller:远端调⽤功能模块
6. Publish-Subscribe:发布订阅功能模块
7. Registry-Discovery:服务注册/发现/上线/下线功能模块
8. Client:基于以上模块整合⽽出的客⼾端模块

1 Network
⽹络通信基于muduo库实现⽹络通信客⼾端
2 Protocol
应⽤层通信协议处理,与服务端保持⼀致。
3 Dispatcher
IO数据分发处理,逻辑与服务端⼀致

4.Requestor 管理请求

Requestor模块存在的意义:针对客⼾端的每⼀条请求进⾏管理,以便于对请求对应的响应做出合适的操作。
⾸先,对于客⼾端来说,不同的地⽅在于,更多时候客⼾端是请求⽅,是主动发起请求服务的⼀⽅,⽽在多线程的⽹络通信中,多线程下,针对多个请求进⾏响应可能会存在时序的问题,这种情况下,则我们⽆法保证⼀个线程发送⼀个请求后,接下来接收到的响应就是针对⾃⼰这条请求的响应,这种情况是⾮常危险的⼀种情况。(请求顺序 ≠ 响应顺序)
其次,类似于Muduo库这种异步IO⽹络通信库,通常IO操作都是异步操作,即发送数据就是把数据放⼊发送缓冲区,但是什么时候会发送由底层的⽹络库来进⾏协调,并且也并不会提供recv接⼝(无法直接阻塞等待响应),⽽是在连接触发可读事件后,IO读取数据完成后调⽤处理回调进⾏数据处理,因此也⽆法直接在发送请求后去等待该条请求的响应。
针对以上问题,我们则创建出当前的请求管理模块来解决,它的思想也⾮常简单,就是给每⼀个请求都设定⼀个请求ID,服务端进⾏响应的时候标识响应针对的是哪个请求(也就是响应信息中会包含请求ID),因此客⼾端这边我们不管收到哪条请求的响应,将数据存储⼊⼀则hash_map中,以请求ID作为映射,并向外提供获取指定请求ID响应的阻塞接⼝,这样只要在发送请求的时候知道⾃⼰的请求ID,那么就能获取到⾃⼰想要的响应,⽽不会出现异常。

  • 请求-响应匹配

    • 在多线程异步环境中,请求顺序 ≠ 响应顺序,无法直接阻塞等待响应,需要 RID 进行匹配。
  • 异步 I/O

    • Muduo 等库不支持 recv 操作,而是 事件触发回调处理,需要 Requestor 统一管理。

针对这个思想,我们再进⼀步,可以将每个请求进⼀步封装描述,添加⼊异步的future控制,或者设置回调函数的⽅式,在不仅可以阻塞获取响应,也可以实现异步获取响应以及回调处理响应。

1️⃣ 客户端调用 `send(request, callback)` 或 `send(request, future)`
2️⃣ `Requestor` 生成 `RID`,将请求存入 `map<rid, describe>`
3️⃣ `Requestor` 发送请求至 `Server`
4️⃣ `Server` 处理请求,并回传 `response(RID, result)`
5️⃣ `Requestor` 收到响应后:
   - `RID` 关联到请求,找到 **回调函数** 或 **Future**
   - 执行 **回调** 或 **解除 Future 阻塞**
组件作用
Requestor负责管理请求,存储 RID → 请求描述
request客户端请求,包含 RIDMTypeBody
describe记录请求信息,可选 callbackfuture
map<rid, describe>记录所有待匹配的请求
onResponse()解析 RID,匹配对应请求,执行回调或解除阻塞
Dispatcher解析 MType,分发不同类型的响应

5.RpcCaller 提供RPC调用接口

RpcCaller客户端 RPC 远程调用的封装模块,它的主要作用是:

  1. 提供统一的 RPC 调用接口
  2. 支持不同的调用模式(同步、异步、回调)
  3. 封装底层 Requestor,发送 RPC 请求并获取响应
  4. 提高易用性,使用户无需关心底层网络通信
调用方式接口特点
同步调用bool call(method, params, response)阻塞等待,直到获取结果
异步调用bool call(method, params, std::future<response>)立即返回,稍后通过 future.get() 获取结果
回调调用bool call(method, params, callback)立即返回,结果返回后自动触发 callback

1️⃣ 用户调用 `call(method, params, ...)`
2️⃣ `RpcCaller` 通过 `Requestor` 发送请求
3️⃣ `Server` 处理请求,返回 `response`
4️⃣ `Requestor` 解析响应,触发 `future.set_value()` 或 `callback`
5️⃣ `RpcCaller` 获取结果,并返回给用户

6.Publish-Subscribe 发布订阅

Publish-Subscribe模块存在意义:向⽤⼾提供发布订阅所需的接⼝,针对推送过来的消息进⾏处理。
发布订阅稍微能复杂⼀丢丢,因为在发布订阅中有两种⻆⾊,⼀个客⼾端可能是消息的发布者,也可能是消息的订阅者。
⽽且不管是哪个⻆⾊都是对主题进⾏操作,因此其中也包含了主题的相关操作,⽐如,要发布⼀条消息需要先创建主题。
且⼀个订阅者可能会订阅多个主题,每个主题的消息可能都会有不同的处理⽅式,因此需要有订阅者主题回调的管理。

Publish-Subscribe(发布订阅)模块用于 消息推送,提供:

  1. 客户端的发布/订阅功能
  2. 主题管理(创建、删除、订阅、取消订阅)
  3. 消息路由,向订阅者分发消息
  4. 回调管理,保证不同主题有独立的回调处理逻辑

📌 主要特点

  • 客户端既可以是 消息发布者,也可以是 订阅者
  • 订阅者可能 同时订阅多个主题
  • 每个主题的 消息处理方式可能不同
  • 需要 确保订阅者能收到正确的主题消息

+------------------------------------------------------+
|              Publish-Subscribe 模块                 |
+------------------------------------------------------+
|  PubSubManager (管理发布 & 订阅)                    |
|  - createTopic(key)  // 创建主题                    |
|  - removeTopic(key)  // 删除主题                    |
|  - subscribe(key, callback) // 订阅主题             |
|  - cancel(key)  // 取消订阅                         |
|  - publish(key, content)  // 发布消息               |
|------------------------------------------------------|
|  Dispatcher  (onPublish 处理 & 分发)                 |
|------------------------------------------------------|
|  Requestor  (发送订阅 & 发布请求到服务器)           |
+------------------------------------------------------+
组件作用
PubSubManager维护 主题订阅者 关系
Dispatcher处理 onPublish() 事件,匹配 map<mtype, handler>
map<key, callback>记录 主题 → 订阅者回调 关系
Requestor负责向 Server 发送 发布/订阅 请求
onPublish()解析 发布请求,调用 订阅者回调

7.Registry-Discovery服务注册与发现

服务注册和发现模块需要实现的功能会稍微复杂⼀些,因为分为两个⻆⾊来完成其功能
1. 注册者:作为Rpc服务的提供者,需要向注册中⼼注册服务,因此需要实现向服务器注册服务的功能
2. 发现者:作为Rpc服务的调⽤者,需要先进⾏服务发现,也就是向服务器发送请求获取能够提供指定服务的主机地址,获取地址后需要管理起来留⽤,且作为发现者,需要关注注册中⼼发送过来的
服务上线/下线消息,以及时对已经下线的服务和主机进⾏管理。

服务注册与发现模块主要包括:

  1. Registrar(注册者)
    • 负责 Rpc 服务提供者 (provider) 向 注册中心 (RegistryServer) 注册自己提供的服务
    • 通过 Requestor 发送 registryService() 请求。
  2. Discoverer(发现者)
    • 负责 Rpc 调用方 (caller) 发现可用的服务提供者 (provider)。
    • 需要定期向 注册中心 发送 serviceDiscovery() 请求获取服务信息。
    • 监听 onServiceNotify() 处理 服务上线/下线通知,保持可用 provider 列表更新。

✅ Registrar 负责 provider 注册服务

✅ Discoverer 负责 caller 发现服务

✅ onServiceNotify() 负责处理 provider 上下线通知

✅ Requestor 负责网络通信,确保请求发送到 RegistryServer

8.Client

以上模块进⾏整合就可以实现各个功能的客⼾端了。

客户端是整个 JSON-RPC 远程调用体系 的调用者,包含四大核心模块:

  1. RegistryClient —— 负责服务注册
  2. DiscoveryClient —— 负责服务发现
  3. RpcClient —— 负责调用 RPC 远程方法
  4. TopicClient —— 负责发布/订阅功能

框架设计

在当前项⽬的实现中,我们将整个项⽬的实现划分为三层来进⾏实现
1. 抽象层:将底层的⽹络通信以及应⽤层通信协议以及请求响应进⾏抽象,使项⽬更具扩展性和灵活性。
2. 具象层:针对抽象的功能进⾏具体的实现。
3. 业务层:基于抽象的框架在上层实现项⽬所需功能。

1.抽象层

Json-RPC 框架 中,我们采用了 Muduo 进行网络通信,并使用 LV 格式协议 解决粘包问题,同时 JSON 作为序列化方式。但:

  • 未来可能更换网络库(如 asiolibevent
  • 可能更换通信协议(如 protobuf 取代 JSON
  • 可能调整底层实现(如 TCP 切换到 UDP

因此,设计一个抽象层,使上层业务代码不依赖具体实现,提升框架的可扩展性。

结构分析


BaseBuffer (数据缓冲)

📌 作用

  • 处理 字节流数据的解析
  • 读取 int32_t 类型的消息头信息(如 Length

📌 核心函数

🔹 readableBytes()           // 可读字节数
🔹 peekInt32()               // 预读取 int32_t,不改变位置
🔹 readInt32()               // 读取 int32_t
🔹 retrieveInt32()           // 取出 int32_t
🔹 retrieveAsString(size_t)  // 取出字符串

提供一个可复用的 Buffer 处理机制


BaseMessage (通用消息抽象)

📌 作用

  • 统一封装 请求 / 响应消息
  • 进行 序列化 / 反序列化
  • 设置消息 ID / 类型

📌 核心函数

🔹 mtype()                            // 获取消息类型
🔹 setMType(MsgType)                  // 设置消息类型
🔹 id()                                // 获取消息 ID
🔹 setId(const std::string &id)        // 设置消息 ID
🔹 serialize(std::string &body)        // 序列化
🔹 unserialize(const std::string &body) // 反序列化
🔹 check()                             // 检查消息完整性

无论是 JSON 还是 protobuf,都可以通过 BaseMessage 进行封装


BaseProtocol (协议层)

📌 作用

  • 解析接收到的 BaseBuffer
  • 检查数据是否完整
  • 负责 onMessage() 回调

📌 核心函数

🔹 onMessage(BaseBufferPtr &, BaseMessagePtr &)  // 解析数据
🔹 serialize(const BaseMessagePtr &)             // 进行序列化
🔹 canProcessed(const BaseBufferPtr &)           // 检查数据是否完整

统一不同的 通信协议 解析流程


BaseConnection (通用连接封装)

📌 作用

  • 处理 发送 / 关闭 / 连接状态
  • 用于 ServerClient 复用

📌 核心函数

🔹 send(const BaseMessagePtr &)  // 发送数据
🔹 shutdown()                    // 关闭连接
🔹 connected()                   // 是否连接

无论是 TCP 还是 UDP,都可以使用 BaseConnection


BaseServer (通用服务器)

📌 作用

  • 提供 连接 / 关闭 / 消息回调
  • 底层可以使用 Muduoasio

📌 核心函数

🔹 setConnectionCallback(const ConnectionCallback &) // 连接回调
🔹 setCloseCallback(const CloseCallback &)         // 关闭回调
🔹 setMessageCallback(const MessageCallback &)     // 消息处理回调
🔹 start()                                         // 启动服务器

Server 具备统一接口,支持不同 网络库


BaseClient (通用客户端)

📌 作用

  • 负责 客户端网络通信
  • 统一 不同 网络库Client 行为

📌 核心函数

🔹 setConnectionCallback(const ConnectionCallback &)  // 连接回调
🔹 setCloseCallback(const CloseCallback &)          // 关闭回调
🔹 setMessageCallback(const MessageCallback &)      // 消息回调
🔹 connect()                                        // 连接
🔹 send(const BaseMessagePtr &)                     // 发送消息
🔹 shutdown()                                       // 关闭
🔹 connected()                                      // 是否连接
🔹 BaseConnectionPtr connection()                   // 获取连接

使 Client 具有统一封装,支持多种 通信方式

结论

BaseBuffer 统一 数据流 处理
BaseMessage 统一 序列化 行为
BaseProtocol 统一 协议解析
BaseConnection 统一 连接管理
BaseServer/BaseClient 统一 网络通信

2.具象层

具象层就是针对抽象的具体实现。
⽽具体的实现也⽐较简单,从抽象类派⽣出具体功能的派⽣类,然后在内部实现各个接⼝功能即可。
• 基于Muduo库实现⽹络通信部分抽象
• 基于LV通信协议实现Protocol部分抽象
不过这⼀层中⽐较特殊的是,我们需要针对不同的请求,从BaseMessage中派⽣出不同的请求和响应类型,以便于在针对指定消息处理时,能够更加轻松的获取或设置请求及响应中的各项数据元素。

3.业务层

业务层就是基于底层的通信框架,针对项⽬中具体的业务功能的实现了,⽐如Rpc请求的处理,发布订阅请求的处理以及服务注册与发现的处理等等。

rpc

整个架构分为三个部分:

  • Server 端 (紫色区域):处理 RPC 逻辑,解析请求并执行业务。
  • Network 层 (黄色区域):负责数据收发,与 Dispatcher 模块交互。
  • Client 端 (绿色区域):提供 RPC 访问接口,向外部应用暴露调用能力。

RPC 请求流程

  1. Client 端调用 RpcCaller.call("Add")
  2. RpcCaller 通过 Requestor 发送请求
  3. Network 层将请求发送到 Server
  4. ServerRpcRouter 解析请求
  5. Dispatcher 分发至 onRpcRequest 处理
  6. 业务逻辑执行完成,Server 端返回结果
  7. Network 层转发至 Client
  8. onRpcResponse 处理响应,并返回给 RpcCaller

发布订阅

该架构图展示了 发布-订阅(Pub-Sub)模式完整交互流程,主要涉及:

  1. Server(紫色区域) - 处理 订阅、取消订阅、消息发布 逻辑
  2. Network(黄色区域) - 负责 通信数据的转发
  3. Client(绿色区域) - 负责 向服务器发送订阅/发布请求,并处理收到的推送消息

整个系统由以下 五大核心组件 组成:

✅ 1. PSManager(发布订阅管理器)

  • Server 端Client 端 各自拥有一个 PSManager
  • ServerPSManager 负责管理 所有的主题和订阅者
  • ClientPSManager 负责 维护本地订阅状态

✅ 2. Dispatcher(请求分发器)

  • Dispatcher 负责将不同的订阅/发布请求分发到对应的 请求处理器
  • 例如:
    • topicSubscribe 请求 -> 订阅一个主题
    • topicCancel 请求 -> 取消订阅
    • onPublish 请求 -> 触发消息发布

✅ 3. Requestor(客户端请求管理)

  • Requestor 管理客户端发出的所有订阅和发布请求
  • 维护 请求 ID,确保正确匹配 响应数据
  • 例如:
    • 发送 topicSubscribe 请求时,Requestor 记录该请求
    • 当服务器返回订阅确认时,Requestor 处理该响应

✅ 4. Network(网络通信模块)

  • 负责 ClientServer 之间的 数据传输
  • 作用:
    • 发送 topicSubscribetopicPublish 请求
    • 转发 服务器推送的消息给 Client

✅ 5. Publish(消息发布处理器)

  • 负责 将消息推送给订阅该主题的客户端
  • 例如:
    • PSManager 发现 Topic_A 有新消息
    • Publish 找到所有订阅 Topic_A 的客户端
    • 通过 Network 发送推送消息

数据流分析

✅ 1. 订阅主题

📝 场景: Client 订阅 Topic_A

  1. Client 端
    • topicSubscribe("Topic_A") 被调用
    • Requestor 发送 订阅请求
    • Network 将请求 转发至 Server
  2. Server 端
    • Dispatcher 接收 topicSubscribe 请求
    • PSManager 记录该 客户端对 Topic_A 的订阅
    • 返回订阅成功 响应给 Client
  3. Client 端
    • onTopicResponse 处理订阅响应
    • 订阅成功 ✅

✅ 2. 发布消息

📝 场景: ClientTopic_A 发布一条消息

  1. Client 端

    • onPublish("Topic_A", "Hello!") 被调用
    • Requestor 发送 消息发布请求
    • Network 将请求发送给 Server
  2. Server 端

    • Dispatcher 接收 onPublish 请求
    • PSManager 检查 哪些客户端订阅了 Topic_A
    • 通过 Publish 模块,将消息发送给订阅者
  3. Client 端

    • 订阅 Topic_A 的客户端 收到推送消息
    • onTopicResponse 处理消息推送 ✅

✅ 3. 取消订阅

📝 场景: Client 取消订阅 Topic_A

  1. Client 端

    • topicCancel("Topic_A") 被调用
    • Requestor 发送 取消订阅请求
    • Network 将请求发送给 Server
  2. Server 端

    • Dispatcher 接收 topicCancel 请求
    • PSManager 删除该客户端的 订阅记录
    • 返回取消成功 响应给 Client
  3. Client 端

    • onTopicResponse 处理取消订阅响应 ✅

Server 端 (PSManager) 负责:管理 主题 & 订阅者
Client 端 (PSManager) 负责:维护 本地订阅状态
Dispatcher 负责:分发请求到正确的 订阅/发布处理器
Requestor 负责:管理请求,并匹配服务器的 响应
Publish 负责:将消息推送给 所有订阅该主题的客户端

服务注册&发现

该架构图展示了 服务注册 & 发现(Registry-Discovery, RD)完整交互流程,主要涉及:

  1. Server(紫色区域) - 负责 服务注册、发现 & 维护
  2. Network(黄色区域) - 负责 数据转发
  3. Client(绿色区域) - 负责 服务注册、发现 & 监听变更

整个系统由以下 六大核心组件 组成:

✅ 1. RDManager(注册发现管理器)

  • Server 端Client 端 各自拥有一个 RDManager
  • ServerRDManager 负责管理 所有服务信息
  • ClientRDManager 负责 维护本地服务状态

✅ 2. Provider(服务提供者)

  • 代表 提供 RPC 服务的节点
  • 通过 serviceRegistry()RDManager 注册自己
  • 断开连接后,会 触发 offlineNotify 通知 Client

✅ 3. Discoverer(服务发现者)

  • 代表 需要调用远程服务的客户端
  • 通过 serviceDiscovery() 询问 RDManager 可用的服务
  • 监听 onlineNotifyofflineNotify,获取 服务上下线事件

✅ 4. Dispatcher(请求分发器)

  • Dispatcher 负责将不同的 服务发现 & 变更通知请求 分发到对应的处理器
  • 例如:
    • onRegistry 处理 服务注册
    • onDiscovery 处理 服务发现
    • onlineNotify & offlineNotify 处理 服务上下线

✅ 5. Requestor(请求管理)

  • 管理客户端发出的所有 RD 相关请求
  • 维护 请求 ID,确保正确匹配 响应数据
  • 例如:
    • 发送 serviceDiscovery() 请求时,Requestor 记录该请求
    • 当服务器返回服务信息时,Requestor 处理该响应

✅ 6. Network(网络通信)

  • 负责 ClientServer 之间的 RD 数据传输
  • 作用:
    • 发送 serviceRegistry()serviceDiscovery() 请求
    • 转发 服务器推送的 服务上下线通知

数据流分析

✅ 1. 服务注册

📝 场景: Provider 注册服务

  1. Client 端

    • Provider 调用 serviceRegistry("Add", host:1234)
    • Requestor 发送 注册请求
    • Network 将请求转发至 Server
  2. Server 端

    • Dispatcher 解析 serviceRegistry
    • RDManager 记录 Add 方法的可用 host
    • 返回注册成功 响应给 Client
  3. Client 端

    • onRDResponse 处理注册成功响应 ✅

✅ 2. 服务发现

📝 场景: Discoverer 查询可用 Add 服务

  1. Client 端

    • serviceDiscovery("Add") 被调用
    • Requestor 发送 发现请求
    • Network 将请求转发至 Server
  2. Server 端

    • Dispatcher 解析 serviceDiscovery
    • RDManager 查询 当前可用的 Add 提供者
    • 返回 Add 提供者列表Client
  3. Client 端

    • onRDResponse 处理 发现响应
    • Discoverer 记录可用 服务提供者列表
    • 成功找到 Add 服务可用节点

✅ 3. 服务上线通知

📝 场景: Provider 新增可用 Add 服务

  1. Server 端
    • ProviderRDManager 注册新的 Add 服务
    • RDManager 发现 Discoverer 查询过 Add
    • 发送 onlineNotify 给所有 Discoverer
  2. Client 端
    • onRDNotify 处理 新服务上线通知
    • Discoverer 更新可用 Add 提供者列表

✅ 4. 服务下线通知

📝 场景: Provider 断开连接,Add 服务下线

  1. Server 端
    • Provider 断开,RDManager 检测到 Add 服务不可用
    • RDManager 向所有 Discoverer 发送 offlineNotify
  2. Client 端
    • onRDNotify 处理 服务下线通知
    • Discoverer 更新可用 Add 提供者列表

相关文章:

C++ - 从零实现Json-Rpc框架-2(服务端模块 客户端模块 框架设计)

项⽬设计 本质上来讲&#xff0c;我们要实现的rpc&#xff08;远端调⽤&#xff09;思想上并不复杂&#xff0c;甚⾄可以说是简单&#xff0c;其实就是客⼾端想要完成某个任务的处理&#xff0c;但是这个处理的过程并不⾃⼰来完成&#xff0c;⽽是&#xff0c;将请求发送到服务…...

课程5. 迁移学习

课程5. 迁移学习 卷积神经网络架构ImageNet神经网络架构实践从 torchvision 加载模型在一个图像上测试预先训练的网络 迁移学习网络训练冻结层实践准备数据替换网络的最后一层冻结层网络训练获取测试样本的质量指标 课程计划&#xff1a; 流行的神经网络架构迁移学习 卷积神经…...

SATA(Serial Advanced Technology Attachment)详解

一、SATA的定义与核心特性 SATA&#xff08;串行高级技术附件&#xff09;是一种 用于连接存储设备&#xff08;如硬盘、固态硬盘、光驱&#xff09;的高速串行接口标准&#xff0c;取代了早期的PATA&#xff08;并行ATA&#xff09;。其核心特性包括&#xff1a; 高速传输&am…...

常用的 MyBatis 标签及其作用

MyBatis 是一个优秀的持久层框架&#xff0c;它通过 XML 或注解的方式将 Java 对象与数据库操作进行映射。在 MyBatis 的 XML 映射文件中&#xff0c;可以使用多种标签来定义 SQL 语句、参数映射、结果映射等。以下是一些常用的 MyBatis 标签及其作用&#xff1a; 1. 基本标签 …...

Blender配置渲染设置并输出动画

在Blender中&#xff0c;渲染设置和渲染动画的选项位于不同的面板中。以下是具体步骤&#xff1a; 渲染设置 渲染设置用于配置输出格式、分辨率、帧率等参数。 打开右侧的 属性面板&#xff08;按 N 键可切换显示&#xff09;。 点击 “输出属性” 选项卡&#xff08;图标是…...

网络故障排查指南:分治法与排除法结合的分层诊断手册

目录 一、排查方法论&#xff1a;分治法与排除法的结合 1. 分治法&#xff08;Divide and Conquer&#xff09; 2. 排除法&#xff08;Elimination&#xff09; 二、分层诊断实战手册 1. 物理层排查&#xff08;设备与线路&#xff09; 硬件检测三板斧 运维经验 2. 网络…...

【万字总结】前端全方位性能优化指南(三)——GPU渲染加速、WebGPU、OffscreenCanvas多线程渲染

theme: condensed-night-purple 前言 当每秒60帧的流畅渲染遭遇百万级多边形场景,传统CPU绘图如同单车道上的赛车——即便引擎轰鸣,依然难逃卡顿困局。现代GPU加速技术将渲染任务从「单车道」扩展到「八车道」,本章以分层爆破、API革命、线程联邦为技术支柱,拆解如何通过G…...

报错 - redis - Unit redis.service could not be found.

报错&#xff1a; Unit redis.service could not be found.Could not connect to Redis at 127.0.0.1:6379: Connection refused解决方法&#xff1a; 检查状态、有必要的话 重新安装 Linux 上查看状态 systemctl status redis显示以下内容&#xff0c;代表正常服务 出现下面…...

Windows系统本地部署OpenManus对接Ollama调用本地AI大模型

文章目录 前言1. 环境准备1.1 安装Python1.2. 安装conda 2. 本地部署OpenManus2.1 创建一个新conda环境2.2 克隆存储库2.3 安装依赖环境 3. 安装Ollama4. 安装QwQ 32B模型5. 修改OpenManus配置文件6. 运行OpenManus7.通过网页使用OpenManus8. 安装内网穿透8.1 配置随机公网地址…...

【递归,搜索与回溯算法篇】- 名词解释

一. 递归 1. 什么是递归&#xff1f; 定义&#xff1a; 函数自己调用自己的情况关键点&#xff1a; ➀终止条件&#xff1a; 必须明确递归出口&#xff0c;避免无限递归 ➁子问题拆分&#xff1a; 问题需能分解成结构相同的更小的子问题缺点&#xff1a; ➀栈溢出风险&#x…...

【设计模式】装饰模式

六、装饰模式 装饰(Decorator) 模式也称为装饰器模式/包装模式&#xff0c;是一种结构型模式。这是一个非常有趣和值得学习的设计模式&#xff0c;该模式展现出了运行时的一种扩展能力&#xff0c;以及比继承更强大和灵活的设计视角和设计能力&#xff0c;甚至在有些场合下&am…...

c库、POSIX库、C++库、boost库之间的区别和联系

文章目录 一、区别1. 定义和来源2. 功能范围3. 可移植性4. 语言支持5. 维护和更新 二、联系1. 相互补充2. 部分功能重叠3. 共同促进编程发展4. 代码兼容性 三、总结 一、区别 1. 定义和来源 C 库函数&#xff1a;由 ANSI C 和 ISO C 标准定义&#xff0c;是 C 语言编程的基础…...

算法及数据结构系列 - 树

系列文章目录 算法及数据结构系列 - 二分查找 算法及数据结构系列 - BFS算法 算法及数据结构系列 - 动态规划 算法及数据结构系列 - 双指针 算法及数据结构系列 - 回溯算法 文章目录 树框架树遍历框架N叉树遍历框架 经典题型124.二叉树的最大路径和105.从前序与中序遍历序列构造…...

go安装lazydocker

安装 先安装go环境 https://blog.csdn.net/Yqha1/article/details/146430281?fromshareblogdetail&sharetypeblogdetail&sharerId146430281&sharereferPC&sharesourceYqha1&sharefromfrom_link 安装lazydocker go install github.com/jesseduffield/laz…...

《深度学习》——YOLOv3详解

文章目录 YOLOv3简介YOLOv3核心原理YOLOv3改进YOLOv3网络结构 YOLOv3简介 YOLOv3&#xff08;You Only Look Once, version 3&#xff09;是一种先进的实时目标检测算法&#xff0c;由 Joseph Redmon 和 Ali Farhadi 开发。它在目标检测领域表现出色&#xff0c;具有速度快、精…...

使用spring-ai-ollama访问本地化部署DeepSeek

创建SpringBoot工程&#xff0c;引入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"htt…...

Kafka消息自定义序列化

文章目录 1. 默认序列化2.自定义序列化3.示例4.自定义解序列化器 1. 默认序列化 在网络中发送数据都是以字节的方式&#xff0c;Kafka也不例外。Apache Kafka支持用户给broker发送各种类型的消息。它可以是一个字符串、一个整数、一个数组或是其他任意的对象类型。序列化器(se…...

使用Systemd管理ES服务进程

Centos中的Systemd介绍 CentOS 中的 Systemd 详细介绍 Systemd 是 Linux 系统的初始化系统和服务管理器&#xff0c;自 CentOS 7 起取代了传统的 SysVinit&#xff0c;成为默认的初始化工具。它负责系统启动、服务管理、日志记录等核心功能&#xff0c;显著提升了系统的启动速…...

编程语言选择分析:C#、Rust、Go 与 TypeScript 编译器优化

编程语言选择分析&#xff1a;C#、Rust、Go 与 TypeScript 编译器优化 在讨论编程语言的选择时&#xff0c;特别是针对微软的 C# 和 Rust&#xff0c;以及谷歌的 Go 语言&#xff0c;以及微软试图通过 Go 来拯救 TypeScript 编译器的问题&#xff0c;我们可以从多个角度来分析和…...

使用粘贴控件

HarmonyOS 5.0.3(15) 版本的配套文档&#xff0c;该版本API能力级别为API 15 Release 文章目录 约束与限制开发步骤 粘贴控件是一种特殊的系统安全控件&#xff0c;它允许应用在用户的授权下无提示地读取剪贴板数据。 在应用集成粘贴控件后&#xff0c;用户点击该控件&#xf…...

MySQL 客户端连不上(1045 错误)原因全解析

MySQL 客户端连不上(1045 错误)原因全解析 在我们学习 MySQL 或从事 MySQL DBA 工作期间,时常会遇到:“我尝试连接到 MySQL 并且收到1045 错误,但我确定我的用户和密码都没问题”。 不管你现在是否是高手还是高高手,都不可避免曾经在初学的时候犯过一些很初级的错误,例…...

麒麟系列Linux发行版探秘

以下内容摘自《银河麒麟操作系统进阶应用》一书。 银河麒麟操作系统&#xff08;Kylin&#xff09; 银河麒麟&#xff08;Kylin&#xff09;操作系统是中国自主研发的一款基于Linux内核的操作系统。它的发展历程可以追溯到2002年&#xff0c;最初由国防科技大学主导研发&…...

刘强东突然发声:不该用算法压榨最底层兄弟!东哥,真正的人民企业家

今天忙了一天&#xff0c;很累&#xff0c;准备睡觉的时候&#xff0c;看到网上盛传的刘强东的朋友圈&#xff0c;东哥又在朋友圈发文了。 说实话&#xff0c;看完之后&#xff0c;感动&#xff0c;真的感动。 尤其是当我看到这两句话的时候。 1、我们所学的知识、商业模式、技…...

信息收集与问答系统流程分析与改进建议

现有系统的问题与局限 1. 资源管理问题 二元决策机制过于简化&#xff1a;当前系统仅在令牌预算耗尽时才进入Beast Mode&#xff0c;缺乏渐进式资源分配策略缺少早期预算规划&#xff1a;没有基于问题复杂度的初始资源分配机制缺乏优先级资源分配&#xff1a;所有问题和策略消…...

【人工智能】如何理解transformer中的token?

如何理解transformer中的token? **一、Token在Transformer中的作用****二、文本分词的常见方法****1. 基于词典的分词&#xff08;Dictionary-based Tokenization&#xff09;****2. 子词分词&#xff08;Subword Tokenization&#xff09;****(1) WordPiece算法****(2) BPE&a…...

Spring Boot 集成 Kafka 消息发送方案

一、引言 在 Spring Boot 项目中,Kafka 是常用的消息队列,可实现高效的消息传递。本文介绍三种在 Spring Boot 中使用 Kafka 发送消息的方式,分析各自优缺点,并给出对应的 pom.xml 依赖。 二、依赖引入 在 pom.xml 中添加以下依赖: <dependencies><!-- Sprin…...

Hadoop•HDFS的Java API操作

听说这是目录哦 上传文件到HDFS&#x1f308;一、下载Windows版本的JDK和Hadoop二、配置物理机环境变量三、创建项目四 、添加依赖五、新建java类六、创建文件七、打开集群八、选中、运行 从HDFS下载文件&#x1fa90;一、写代码二、HDFS要个文件三、物理机要个文件夹&#xff…...

电脑如何设置几分钟后自动关机

摘要&#xff1a;本文提供Windows、macOS和Linux系统设置定时自动关机的详细方法。 目录 一、Windows系统设置方法 设置定时关机 取消关机计划 二、macOS系统设置方法 设置定时关机取消关机计划 三、Linux系统设置方法 设置定时关机 取消关机计划 四、注意事项五、扩展&#x…...

固定公网 IP

固定公网 IP 是指为用户分配一个长期不变且可从互联网直接访问的 IP 地址&#xff0c;具有以下重要作用&#xff1a; 1. 搭建服务器 网站托管&#xff1a;可用于托管网站、博客或电子商务平台。 应用服务器&#xff1a;支持运行邮件服务器、游戏服务器、数据库等。 远程访问&…...

Linux安装go环境

安装一个lazydocker&#xff0c;根据文档需要先安装go环境 https://github.com/jesseduffield/lazydocker 官方文档解析 https://go.dev/doc/install 文档内容如下&#xff0c;一共三步 1.删除先前安装的go&#xff0c;解压下载的go压缩包到/usr/local目录 2.添加环境变量&…...

Git的基本使用

Git的基本使用 前言 &#xff1a;为什么使用GitGit基本操作1. 初始化2. Git分区3. 认识.git目录4. git基本操作 Git分支管理1. 基本操作2. Git分支设计规范 Git 标签管理1. Git标签的使用2. 标签使用规范3. Git标签与分支的区别 分离头指针问题1. 分离头指针问题的风险2. 分离头…...

鸿蒙Flutter开发故事:不,你不需要鸿蒙化

在华为牵头下&#xff0c;Flutter 鸿蒙化如火如荼进行&#xff0c;当第一次看到一份上百个插件的Excel 列表时&#xff0c;我也感到震惊&#xff0c;排名前 100 的插件赫然在列&#xff0c;这无疑是一次大规模的军团作战。 然后&#xff0c;参战团队鱼龙混杂&#xff0c;难免有…...

Mysql:关于命名

1. 命名的对象&#xff1a;库名、表名、列名、索引名 2. 用反引号包裹的情况下&#xff0c;命名时不允许使用空白字符、反引号&#xff0c;其它字符均可 3. 无反引号包裹的情况下&#xff0c;命名时仅允许使用&#xff1a;$、_、数字、大小写字母、中文字符(已知win系统支持)…...

JAVA————十五万字汇总

JAVA语言概述 JAVA语句结构 JAVA面向对象程序设计&#xff08;一&#xff09; JAVA面向对象程序设计&#xff08;二&#xff09; JAVA面向对象程序设计&#xff08;三&#xff09;工具类的实现 JAVA面向对象程序设计&#xff08;四&#xff09;录入异常处理 JAVA图形用户界面设…...

Chrome-Edge-IDEA-Win 常用插件-工具包

Chrome-Edge-IDEA-Win 常用插件-工具包 Chrome-Edge-IDEA-Win 常用插件-工具包谷歌插件chropathJSONViewOctotree - GitHub code treeXPath Helper书签侧边栏篡改猴Print Edit WEEdge浏览器插件IDEA插件CodeGlance Pro 代码迷你缩放图插件Alibaba Cloud ToolkitAlibaba Java Co…...

DeepSeek-R1论文深度解析:纯强化学习如何引爆LLM推理革命?

技术突破&#xff1a;从“无监督”到“自主进化”的跨越 paper &#xff1a;https://arxiv.org/pdf/2501.12948目录 技术突破&#xff1a;从“无监督”到“自主进化”的跨越1 DeepSeek-R1-Zero&#xff1a; RLnoSFT1.1 R1-Zero&#xff1a; GRPO&#xff08;Group Relative Po…...

最新!Ubuntu Docker 安装教程

源自: AINLPer&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2025-3-1 更多&#xff1a;>>>>大模型/AIGC、学术前沿的知识分享&#xff01; 看到很多部署大模型的时候&#xff0c;都是基于docker安装部署的。…...

【Javascrip】Javascript练习01 REST API using Express.js.

针对该问题的项目路径 要求部分 what you need to doReview the tasks provided in the section below.Obtain the boilerplate code.Use your local development environment to implement a solution.Upload your solution for marking via Gradescope. There is no attempt…...

visual studion 2022如何使用PlaySound()

书籍&#xff1a;《windows程序设计(第五版)》的开始 环境&#xff1a;visual studio 2022 内容&#xff1a;HELLOWIN程序 说明&#xff1a;以下内容大部分来自腾讯元宝。 在Visual Studio 2022中使用PlaySound()函数播放音频&#xff0c;需完成以下步骤&#xff1a; ​1. 配…...

C++相关基础概念之入门讲解(下)

1. 引用 ​ int main() {const int a10;int& aaa;aa;cout<<aa<<endl; } 引用 不是新定义一个变量&#xff0c;而 是给已存在变量取了一个别名 &#xff0c;编译器不会为引用变量开辟内存空 间&#xff0c;它和它引用的变量 共用同一块内存空间&#xff08;初…...

从零开始学可靠消息投递:分布式事务的“最终一致性”方案

一、什么是可靠消息投递&#xff1f;—— 消息队列的“防丢宝典” 可靠消息投递 是指通过消息队列&#xff08;如 RocketMQ&#xff09;确保消息在生产、传输、消费过程中不丢失、不重复、有序到达。其核心目标是在分布式系统中保障数据最终一致性&#xff0c;常用于订单处理、…...

生物化学笔记:医学免疫学原理 免疫系统的组成与功能+克隆选择学说

免疫系统的组成与功能 克隆选择学说 克隆选择学说&#xff08;Clonal Selection Theory&#xff09;是免疫学的核心理论之一&#xff0c;由 麦克法兰伯内特&#xff08;Frank Macfarlane Burnet&#xff09; 在 1957 年提出&#xff0c;用于解释特异性免疫反应的机制。 基本概…...

SpringBoot最佳实践之 - 使用AOP记录操作日志

1. 前言 本篇博客是个人在工作中遇到的需求。针对此需求&#xff0c;开发了具体的实现代码。并不是普适的记录操作日志的方式。以阅读本篇博客的朋友&#xff0c;可以参考此篇博客中记录日志的方式&#xff0c;可能会对你有些许帮助和启发。 2. 需求描述 有一个后台管理系统…...

MySql中 一条select语句的执行流程

一条 SELECT 语句的执行流程涉及到数据库管理系统&#xff08;DBMS&#xff09;的多个组件和阶段。以下是一个更为详细的执行流程&#xff0c;以关系型数据库&#xff08;如 MySQL、PostgreSQL 等&#xff09;为例&#xff1a; 1. 客户端发送查询 用户输入&#xff1a;用户在客…...

图论——kruskal算法

53. 寻宝(第七期模拟笔试) 题目描述 在世界的某个区域,有一些分散的神秘岛屿,每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路,方便运输。 不同岛屿之间,路途距离不同,国王希望你可以规划建公路的方案,如何可以以最短的总公路距离将 所有岛屿联通…...

【dify】 dify环境变量配置说明

这是一份Dify平台的环境变量配置文件&#xff0c;对平台的各项功能、服务和组件进行参数设置。以下是对其主要部分的详细解读&#xff1a; 1. 通用变量&#xff08;Common Variables&#xff09; CONSOLE_API_URL&#xff1a;控制台API的后端URL&#xff0c;用于拼接授权回调…...

如何在 Vue 项目中实现动态组件加载,有什么应用场景?

大白话如何在 Vue 项目中实现动态组件加载&#xff0c;有什么应用场景&#xff1f; 什么是动态组件加载 在 Vue 项目里&#xff0c;动态组件加载就是能够在程序运行时动态地决定要渲染哪个组件。打个比方&#xff0c;就像你去餐馆点菜&#xff0c;不同的时间你可能想吃不同的…...

FRP在物联网设备中的穿透方案

物联网设备常位于NAT后&#xff0c;FRP为其提供稳定穿透链路。 配置要点 轻量化部署&#xff1a;使用ARM版本FRP客户端&#xff0c;适配树莓派等设备9。 自启动脚本&#xff1a;通过systemd或crontab实现设备重启后自动连接26。 低功耗优化&#xff1a;调整心跳间隔&#xf…...

Android 13深度定制:SystemUI状态栏时间居中显示终极实战指南

一、架构设计与技术解析 1. SystemUI状态栏核心布局机制 层级结构 mermaid 复制 graph TDPhoneStatusBarView --> StatusBarContents[status_bar_contents]StatusBarContents --> LeftLayout[status_bar_left_side]StatusBarContents --> ClockLayout[Clock控件]Left…...

Python实战(3)-数据库操作

前面说过&#xff0c;可用的SQL数据库引擎有很多&#xff0c;它们都有相应的Python模块。这些数据库引擎大都作为服务器程序运行&#xff0c;连安装都需要有管理员权限。为降低Python DB API的使用门槛&#xff0c;我选择了一个名为SQLite的小型数据库引擎。它不需要作为独立的…...