【从零实现Json-Rpc框架】- 项目设计篇
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 📢前言
- 🏳️🌈1 理解项目功能
- 🏳️🌈2 框架设计
- 2.1 服务端模块划分
- 2.1.2 NetWork:网络通信模块
- 2.1.3 Protocol: 应用层通信协议模块
- 2.1.4 Dispatcher: 消息分发处理模块
- 2.1.5 RpcRouter: 远端调用路由功能模块
- 2.1.6 Publish-Subscribe: 发布订阅功能模块
- 2.1.7 Registry-Discovery: 服务注册/发现/上线/下线功能模块
- 2.1.8 Server: 基于以上模块整合而出的服务端模块
- 2.2 客户端模型划分
- 2.2.1 Protocol: 应用层通信协议模块
- 2.2.2 Network: 网络通信模块
- 2.2.3 Dispatcher: 消息分发处理模块
- 2.2.4 Requestor: 请求管理模块
- 2.2.5 RpcCaller: 远端调用功能模块
- 2.2.6 Publish-Subscribe: 发布订阅功能模块
- 2.2.7 Registry-Discovery: 服务注册/发现/上线/下线功能模块
- 2.2.8 Client: 基于以上模块整合而出的客户端模块
- 2.3 框架设计
- 2.3.1 抽象层
- 2.3.2 具象层
- 2.3.3 业务层
- 2.3.4 整体框架设计
- 👥总结
📢前言
截至当前,笔者已经将所有基础要用的插件等都装好了,也较为详细地介绍了下几个第三方库
那么这篇,笔者就来介绍一下我们该如何设计一个 Json-Rpc框架
🏳️🌈1 理解项目功能
本质上来讲,我们要实现的rpc(远端调用)思想上并不复杂,甚至可以说是简单,其实就是客户端想要完成某个任务的处理,但是这个处理的过程并不自己来完成,而是,将请求发送到服务器上,让服务器来帮其完成处理过程,并返回结果,客户端拿到结果后返回。
然而上图的模型中,是一种多对一或一对一的关系,一旦服务端掉线,则客户端无法进行远端调用,目其服务端的负载也会较高,因此在rpc实现中,我们不仅要实现其基本功能,还要再进一步,实现分布式架构的rpc。
分布式架构:简单理解就是由多个节点组成的一个系统,这些节点通常指的是服务器,将不同的业务或者同一个业务拆分分布在不同的节点上,通过协同工作解决高并发的问题,提高系统扩展性和可用性。
其实现思想也并不复杂,也就是在原来的模型基础上,增加一个注册中心,基于注册中心不同的服务提供服务器向注册中心进行服务注册,相当于告诉注册中心自己能够提供什么服务,而客户端在进行远端调用前,先通过注册中心进行服务发现,找到能够提供服务的服务器,然后发起调用。
而其次的发布订阅功能,则是依托于多个客户端围绕服务端进行消息的转发。不过单纯的消息转发功能,并不能满足于大部分场景的需要,因此会在其基础上实现基于主题订阅的转发。
基于以上功能的合并,我们可以得到一个实现所有功能的结构图
在上图的结构中,我们甚至可以让每一个Server作为备用注册中心形成分布式架构,一旦一个注册中心下线,可以向备用中心进行注册以及请求,且在此基础上客户端在请求Rpc服务的时候,因为可以有多个rpc-provider可选,因此可以实现简单的负载均衡策略,且基于注册中心可以更简便实现发布订阅的功能。
项目的三个主要功能:
- rpc调用
- 服务的注册与发现以及服务的下线/上线通知
- 消息的发布订阅
🏳️🌈2 框架设计
2.1 服务端模块划分
服务端的功能需求:
- 基于网络通信接收客户端的请求,提供rpc服务
- 基于网络通信接收客户端的请求,提供服务注册与发现,上线&下线通知
- 基于网络通信接收客户端的请求,提供主题操作(创建/删除/订阅/取消),消息发布
在服务端的模块划分中,基于以上理解的功能,可以划分出这么几个模块
- Network:网络通信模块
- Protocol:应用层通信协议模块
- Dispatcher:消息分发处理模块
- RpcRouter:远端调用路由功能模块
- Publish-Subscribe:发布订阅功能模块
- Registry-Discovery:服务注册/发现/上线/下线功能模块
- Server:基于以上模块整合而出的服务端模块
2.1.2 NetWork:网络通信模块
应用层通信协议模块的存在意义: 解析数据,解决通信中有可能存在的粘包问题,能够获取到一条完整的消息。
在前边的muduo
库基本使用中,我们能够知道想要让一个服务端/客户端对消息处理,就要设置一个onMessage
的回调函数,在这个函数中对收到的数据进行应用层协议处理。
而Protocol
模块就是是网络通信协议模块的设计,也就是在网络通信中,我们必须设计一个应用层的网络通信协议出来,以解决网络通信中可能存在的粘包问题,而解决粘包有三种方式: 特殊字符间隔,定长,LV格式。
而我们项目中将使用LV格式来定义应用层的通信协议格式
Length: 该字段固定4字节长度,用于表示后续的本条消息数据长度。
MType: 该字段为Value中的固定字段,固定4字节长度,用于表示该条消息的类型。
- Rpc调用请求/响应类型消息。
- 发布/订阅/取消订阅/消息推送类型消息
- 主题创建/删除类型消息。
- 服务注册/发现/上线/下线类型消息。
IDLength: 为消息中的固定字段,该字段固定4字节长度,用于描述后续ID字段的实际长度。
MID: 在每条消息中都会有一个固定字段为ID字段,用于唯一标识消息,ID字段长度不固定。
Body: 消息主题正文数据字段,为请求或响应的实际内容字段。
2.1.3 Protocol: 应用层通信协议模块
模块存在的意义: 区分消息类型,根据不同的类型,调用不同的业务处理函数进行消息处理。
当muduo
库底层通信收到数据后,在onMessage
回调函数中对数据进行应用层协议解析,得到一条实际消息载荷后,我们就该决定这条消息代表这客户端的什么请求,以及应该如何处理。
因此,我们设计出了Dispatcher
模块,作为一个分发模块,这个模块内部会保存有一个hash_map<消息类型,回调函数>
,以此由使用者来决定哪条消息用哪个业务函数进行处理,当收到消息后,在该模块找到其对应的处理回调函数进行调用即可。
2.1.4 Dispatcher: 消息分发处理模块
消息类型:
- rpc请求&响应
- 服务注册/发现/上线/下线请求&响应
- 主题创建/删除/订阅/取消订阅请求&响应,消息发布的请求&响应
2.1.5 RpcRouter: 远端调用路由功能模块
RpcRouter模块存在的意义: 提供rpc请求的处理回调函数,内部所要实现的功能,分辨出客户端请求的服务进行处理得到结果进行响应。
rpc请求中,最关键的两个点:
- 请求方法名称
- 请求对应要处理的参数信息。
在Rpc远端调用中,首先将客户端到服务端的通信链路打通,然后将自己所需要调用的服务名称,以及参数信息传递给服务端,由服务端进行接收处理,并返回结果。
而,不管是客户端要传递给服务端的服务名称以及参数信息,或者服务端返回的结果,都是在上边Protocol
中定义的Bodv字段
中,因此Bodv字段
中就存在了另一层的正文序列化/反席列化过程。
序列化方式有很多种,鉴于当前我们是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
对象中,有什么样的参数,以及参数信息是否符合自己所提供的服务的要求,都应该有一个检测,是否符合要求,符合要求了再取出指定字段的数据进行处理。
因此,对服务端来说,在进行服务注册的时候,必须有一个服务描述,以代码段中的Add请求为例,该服务描述中就应该描述:
服务名称: Add
- 参数名称: num1,是一个整形。
- 参数名称: num2,是一个整形,。
- 返回值类型:整形。
有了这个描述,在回调函数中就可以先对传入的参数进行校验,没问题了则取出指定字段数据进行处理并返回结果
基于以上理解,在实现该模块时,该有以下设计:
- 该模块必须具备一个Rpc路由管理,其中包含对于每个服务的参数校验功能
- 该模块必须具备一个方法名称和方法业务回调的映射
- 该模块必须向外提供 Rpc请求的业务处理函数。
2.1.6 Publish-Subscribe: 发布订阅功能模块
Publish-Subscribe模块存在的意义: 针对发布订阅请求进行处理,提供一个回调函数设置给Dispatcher
模块。
发布订阅所包含的请求操作:
- 主题的创建
- 主题的删除
- 主题的订阅
- 主题的取消订阅
- 主题消息的发布
在当前的项目中,我们也实现一个简单的发布订阅功能,该功能是围绕多个客户端与一个服务端来展开的。
即,任意一个客户端在发布或订阅之前先创建一个主题,比如在新闻发布中我们创建一个音乐新闻主题,哪些客户端希望能够收到音乐新闻相关的消息,则就订阅这个主题,服务端会建立起该主题与客户端之间的联系。
当某个客户端向服务端发布消息,且发布消息的目标主题是音乐新闻主题,则服务端会找出订阅了该主题的客户端,将消息推送给这些客户端。
既然涉及到网络通信,那就先将通信消息的正文格式定义出来:
// 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,
}
功能思想并不复杂,因此我们需要把更多的精力放到其实现设计上:
- 该模块必须具备一个主题管理,且主题中需要保存订阅了该主题的客户端连接。主题收到一条消息,需要将这条消息推送给订阅了该主题的所有客户端
- 该模块必须具备一个订阅者管理,且每个订阅者描述中都必须保存自己所订阅的主题名称’。目的是为了当一个订阅客户端断开连接时,能够找到订阅信息的关联关系,进行删除
- 该模块必须向外提供 主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数
2.1.7 Registry-Discovery: 服务注册/发现/上线/下线功能模块
Registry-Discovery模块存在的意义: 就是针对服务注册与发现请求的处理。
服务注册/发现类型请求中的详细划分
- 服务注册: 服务provider告诉中转中心,自己能提供哪些服务。
- 服务发现: 服务caller询问中转中心,谁能提供指定服务。
- 服务上线: 在一个provider上线了指定服务后,通知发现过该服务的客户端有个provider可以。提供该服务
- 服务下线: 在一个provider断开连接,通知发现过该服务的caller,谁下线了哪个服务
服务注册模块,该模块主要是为了实现分布式架构而存在,让每一个rpc客户端能够从不同的节点主机上获取自己所需的服务,让业务更具扩展性,系统更具健壮性。而为了能够让rpc-caller
知道有哪些rpc-provider
能提供自己所需服务,那么就需要有一个注册中心
让这些rpc-provider
去注册登记自己的服务,让rpc-caller
来发现这些服务:
因此,在我们的服务端功能中,还需实现服务的注册/发现,以及服务的上线/下线功能。
// 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}]
}
该模块的设计如下:
- 必须具备一个服务发现者的管理:
- 方法与发现者: 当一个客户端进行服务发现的时候,进行记录谁发现过该服务,当有一个新的提供者上线的时候,可以通知该发现者
- 连接与发现者: 当一个发现者断开连接了,删除关联关系,往后就不需要通知了
- 必须具备一个服务提供者的管理:
- 连接与提供者: 当一个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,
该主机的该服务下线了 - 方法与提供者: 能够知道谁的哪些方法下线了,然后通知发现过该方法的客户端
- 连接与提供者: 当一个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,
- 必须向
Dispatcher
模块提供一个服务注册/发现的业务处理回调函数
这样,当一个rpc-provider
登记了服务,则将其管理起来,当rpc-caller
进行服务发现时,则将保存的对应服务所对应的主机信息,响应给rpc-caller
。
而,当中途一个rpc-provider
上线登记服务时,则可以给进行了对应服务发现的rpc-caller
进行服务上线通知,通知rpc-caller
当前多了一个对应服务的rpc-provider
。
同时,当一个rpc-provider
下线时,则可以找到进行了该服务发现的rpc-caller
进行服务的下线通知。
2.1.8 Server: 基于以上模块整合而出的服务端模块
当以上的所有功能模块都完成后,我们就可以将所有功能整合到一起来实现服务端程序了。
RpcServer:rpc
: 功能模块与网络通信部分结合。RegistryServer
: 服务发现注册功能模块与网络通信部分结合TopicServer
: 发布订阅功能模块与网络通信部分结合。
2.2 客户端模型划分
在客户端的模块划分中,基于以上理解的功能,可以划分出这么几个块
- Protocol: 应用层通信协议模块
- Network: 网络通信模块
- Dispatcher: 消息分发处理模块
- Requestor: 请求管理模块
- RpcCaller: 远端调用功能模块
- Publish-Subscribe: 发布订阅功能模块
- Registry-Discovery: 服务注册/发现/上线/下线功能模块
- Client: 基于以上模块整合而出的客户端模块
2.2.1 Protocol: 应用层通信协议模块
网络通信基于muduo库实现网络通信客户端
2.2.2 Network: 网络通信模块
应用层通信协议处理,与服务端保持一致。
2.2.3 Dispatcher: 消息分发处理模块
I0数据分发处理,逻辑与服务端一致
2.2.4 Requestor: 请求管理模块
Requestor
模块存在的意义:针对客户端的每一条请求进行管理,以便于对请求对应的响应做出合适的操作。
首先,对于客户端来说,不同的地方在于,更多时候客户端是请求方,是主动发起请求服务的一方,而在多线程的网络通信中,多线程下,针对多个请求进行响应可能会存在时序的问题,这种情况下,则我们无法保证一个线程发送一个请求后,接下来接收到的响应就是针对自己这条请求的响应,这种情况是非常危险的一种情况。
其次,类似于Muduo
库这种异步I0网络通信库,通常10操作都是异步操作,即发送数据就是把数据放入发送缓冲区,但是什么时候会发送由底层的网络库来进行协调,并且也并不会提供recv
接口,而是在连接触发可读事件后,10读取数据完成后调用处理回调进行数据处理,因此也无法直接在发送请求后去等待该条请求的响应。
针对以上问题,我们则创建出当前的请求管理模块来解决,它的思想也非常简单,就是给每一个请求都设定一个请求ID,服务端进行响应的时候标识响应针对的是哪个请求(也就是响应信息中会包含请求ID),因此客户端这边我们不管收到哪条请求的响应,将数据存储入一则hash_map
中,以请求ID作为映射,并向外提供获取指定请求ID响应的阻塞接口,这样只要在发送请求的时候知道自己的请求ID,那么就能获取到自己想要的响应,而不会出现异常。
针对这个思想,我们再进一步,可以将每个请求进一步封装描述,添加入异步的future控制,或者设置回调函数的方式,在不仅可以阳塞获取响应,也可以实现异步获取响应以及回调处理响应。
2.2.5 RpcCaller: 远端调用功能模块
RpcCaller模块存在的意义: 向用户提供进行rpc调用的模块。
Rpc服务调用模块,这个模块相对简单,只需要向外提供几个rpc调用的接口,内部实现向服务端发送请求,等待获取结果即可,稍微麻烦一些的是Rpc调用我们需要提供多种不同方式的调用:
- 同步调用: 发起调用后,等收到响应结果后返回
- 异步调用: 发起调用后立即返回,在想获取结果的时候进行获取
- 回调调用: 发起调用的同时设置结果的处理回调,收到响应后自动对结果进行回调处理
2.2.6 Publish-Subscribe: 发布订阅功能模块
Publish-Subscribe模块存在意义: 向用户提供发布订阅所需的接口,针对推送过来的消息进行处理。
发布订阅稍微能复杂一丢丢,因为在发布订阅中有两种角色,一个客户端可能是消息的发布者
,也可能是消息的订阅者
。
而且不管是哪个角色都是对主题进行操作,因此其中也包含了主题的相关操作,比如,要发布一条消息需要先创建主题。
且一个订阅者可能会订阅多个主题,每个主题的消息可能都会有不同的处理方式,因此需要有订阅者主题回调的管理。
2.2.7 Registry-Discovery: 服务注册/发现/上线/下线功能模块
服务注册和发现模块需要实现的功能会稍微复杂一些,因为分为两个角色来完成其功能
- 注册者: 作为Rpc服务的提供者,需要向注册中心注册服务,因此需要实现向服务器注册服务的功能
- 发现者: 作为Rpc服务的调用者,需要先进行服务发现,也就是向服务器发送请求获取能够提供指定服务的主机地址,获取地址后需要管理起来留用,且作为发现者,需要关注注册中心发送过来的服务上线/下线消息,以及时对已经下线的服务和主机进行管理。
2.2.8 Client: 基于以上模块整合而出的客户端模块
将以上模块进行整合就可以实现各个功能的客户端了。
- RegistryClient: 服务注册功能模块与网络通信客户端结合
- DiscoveryClient: 服务发现功能模块与网络通信客户端结合
- RpcClient: DiscoveryClient&RPC功能模块与网络通信客户端结合
- TopicClient: 发布订阅功能模块与网络通信客户端结合
2.3 框架设计
2.3.1 抽象层
在当前项目的实现中,我们将整个项目的实现划分为三层来进行实现
- 抽象层:将底层的网络通信以及应用层通信协议以及请求响应进行抽象,使项目更具扩展性和灵活性。
- 具象层:针对抽象的功能进行具体的实现。
- 业务层:基于抽象的框架在上层实现项目所需功能。
在咱们的项目实现中,网络通信部分采用了第三方库Muduo库,以及通信协议使用了LV格式的通信协议解决粘包问题,数据正文中采用了Json格式进行序列化和反序列化,而这几方面我们都可能会存在继续优化的可能,甚至在序列化方面不一定非要采用Json,因此在设计项目框架的时候,我们对于底层通信部分相关功能先进行抽象,形成一层抽象层,而上层业务部分根据抽象层来完成功能,这样的好处是在具体的底层功能实现部分,我们可以实现插拔式的模块化替换,以此来提高项目的灵活性和扩展性。
2.3.2 具象层
具象层就是针对抽象的具体实现。
而具体的实现也比较简单,从抽象类派生出具体功能的派生类,然后在内部实现各个接口功能即可。
- 基于Muduo库实现网络通信部分抽象.
- 基于LV通信协议实现Protocol部分抽象。
不过这一层中比较特殊的是,我们需要针对不同的请求,从BaseMessage中派生出不同的请求和响应类型,以便于在针对指定消息处理时,能够更加轻松的获取或设置请求及响应中的各项数据元素。
2.3.3 业务层
业务层就是基于底层的通信框架,针对项目中具体的业务功能的实现了,比如Rpc请求的处理,发布订阅请求的处理以及服务注册与发现的处理等等。
RPC
发布订阅
服务注册&发现
2.3.4 整体框架设计
👥总结
本篇博文对 【从零实现Json-Rpc框架】- 项目设计篇 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:
【从零实现Json-Rpc框架】- 项目设计篇
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
EtherCAT转CANopen配置CANopen侧的PDO映射
EtherCAT转CANopen配置CANopen侧的PDO映射 在工业自动化领域,EtherCAT和CANopen是两种广泛应用的通信协议。它们各自具有独特的优势,但在某些应用场景下,需要将这两种协议进行转换以实现设备间的高效数据交换。本文将详细介绍如何在使用Ethe…...
Vite管理的Vue3项目中monaco editer的使用以及组件封装
文章目录 背景环境说明安装流程以及组件封装引入依赖封装组件 外部使用实现效果 v-model实现原理 背景 做oj系统的时候,需要使用代码编辑器,决定使用Monaco Editor,但是因为自身能力问题,读不懂官网文档,最终结合ai和网友的帖子成功引入&…...
完整的类在JVM中的生命周期详解
首先给出一个示例代码: 示例的目标是展示一个多功能的类结构,包含继承、接口实现、静态成员、本地方法、线程安全等特性,同时模拟一个简单的“计算器”场景,计算并管理数字。(尽量将所有的 Java 组件和关键字都给出&am…...
大数据学习栈记——HBase操作(shell java)
本文介绍HBase在shell终端的常见操作以及如何利用java api操作HBase,操作系统:Ubuntu24.04 参考: https://blog.51cto.com/u_16099228/8016429 https://blog.csdn.net/m0_37739193/article/details/73618899 https://cloud.tencent.com/d…...
【商城实战(65)】退换货流程全解析:从前端到后端的技术实现
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…...
改进BM25稀疏检索和BGE稠密检索
改进BM25稀疏检索和BGE稠密检索 检索算法层面 混合检索策略优化 自适应加权融合:在BM25和BGE等混合检索时,根据查询文本的特征(如长度、专业术语占比等)动态调整两者的权重。例如,对于包含大量专业术语的查询,增加BGE的权重;对于关键词明确的简单查询,增加BM25的权重。…...
WPS二次开发系列:以自动播放模式打开PPT文档
在前面文章中 android 调用wps打开文档并感知保存事件 介绍了如何使用WPS SDK打开文档,那么我们是否能够实现在打开WPS 文档的时候能够传递一些参数来控制打开文档的行为呢,经过研究WPS SDK相关文档和API,最终实现了 以自动播放方式打开PPT文…...
当AI重构编程范式:Java 24的进化逻辑与技术深水区的战略突围
一、语言进化的底层密码:从“工具适配”到“定义规则” 在2025年3月19日发布的Java 24中,Oracle以"30周年技术宣言"的姿态展示了编程语言进化的新范式。该版本不仅包含模式匹配、结构化并发等21项JEP特性,更通过后量子加密、AI原生…...
air780eq 阿里云
硬件:APM32F030C8 Air 780eq 参考文档: 合宙780E-4G模块通过AT指令连接到阿里云平台,实现信息的收发_air780e上传阿里云属性值at命令-CSDN博客 阿里云 - atair780eq - 合宙文档中心 4G模块接入阿里云-实现数据上传和命令下发_4g模块上传…...
网络安全之vlan实验
在对vlan进行一定的学习之后我们来练习一个小实验来加深理解记忆 首先是对实验进行一个搭建 第一部分:给交换机配置vlan 首先是sw1 [Huawei]vlan batch 2 to 5 [Huawei]int g0/0/1 [Huawei-GigabitEthernet0/0/1]port hybrid tagged vlan 2 [Huawei-GigabitEthe…...
mac命令行快捷键
光标移动 Ctrl A: 将光标移动到行首。Ctrl E: 将光标移动到行尾。Option 左箭头: 向左移动一个单词。Option 右箭头: 向右移动一个单词。 删除和修改 Ctrl K: 删除从光标到行尾的所有内容。Ctrl U: 删除从光标到行首的所有内容。Ctrl W: 删除光标前的一个单词。Ctrl …...
计算机网络 - OSI 七层模型
OSI 七层模型 OSI(Open System Interconnection,开放系统互联)模型由 ISO(国际标准化组织) 制定,目的是为不同计算机网络系统之间的通信提供一个标准化的框架。它将网络通信划分为 七个层次,每…...
TCP/IP 协议族详细知识点清单
📚 TCP/IP 协议族详细知识点清单 一、概述与体系结构 🌐 TCP/IP 协议模型(四层模型) 层次协议功能应用层HTTP、FTP、DNS、SMTP提供应用服务传输层TCP、UDP端到端传输,可靠或不可靠网络层IP、ICMP、ARP、RARP寻址、路…...
Vue3(自定义指令directive详解)
文章目录 前言一、自定义指令的生命周期钩子二、自定义指令的创建与注册使用三、扩展 简化形式总结 前言 在Vue3中,自定义指令是一种强大的工具,允许开发者扩展和增强HTML元素的功能。以下是对Vue3中自定义指令的详细解析: 一、自定义指令…...
Redis--redis客户端
目录 一、引言 二、数据库管理命令 三、redis客户端 四、Java客户端使用Redis 五、相关命令使用 1.get,set 2.exists,del 3.keys 4.expire,ttl 六、总结 一、引言 在之前学了redis相关类型命令之后,本篇文章,…...
【高项】信息系统项目管理师(十)项目风险管理【5分】
项目风险是一种不确定的事件或条件,一旦发生,会对项目目标产生某种正面或负面的影响。项目风险既包括对项目目标的威胁,也包括促进项目目标的机会。已知风险是那些已经经过识别和分析的风险,对于已知风险,对其进行规划,寻找应对方案是可行的;虽然项目经理们可以依据以往…...
jenkins批量复制视图项目到新的视图
1、当前视图为 测试2分支,创建了新的视图为国际化预生产 2、进入系统设置的脚本管理 import hudson.model.* //源view def str_view "测试2分支" //目标view def str_new_view "国际化预生产" //源job名称(模糊匹配) def str_search &qu…...
uv:Rust 驱动的 Python 包管理新时代
在 Python 包管理工具层出不穷的今天,pip、pip-tools、poetry、conda 等各有千秋。而今天要介绍的 uv,则是一款由 Astral 团队推出、采用 Rust 编写的全新工具,目标直指成为 “Python 的 Cargo”。它不仅在性能上表现优异,而且在功…...
GD32 ISP下载程序(串口烧录)
一、下载烧录软件 下载地址兆易创新GigaDevice-资料下载兆易创新GD32 MCUhttps://www.gd32mcu.com/cn/download?kwGD32All-In-OneProgrammer&lancn 二、使用USB转串口连接GD32开发板 这里使用GD32E230C8T6为例: GD32E230C8T6USB 转串口模块说明PA9ÿ…...
Spring MVC 配置详解与入门案例
目录 引言 一、Spring MVC 的发展背景 1. Model I 与 Model II 2. MVC 模式 二、Spring MVC 入门案例 1. 创建 WEB 工程并引入依赖 2. 配置 web.xml 3. 配置 springmvc.xml 4. 创建控制器和视图 5. 部署并测试 三、Spring MVC 原理 1. 核心组件 2. 请求处理流程 …...
【10万QPS压力测试】Redis三主三从高可用集群基准测试
📕我是廖志伟,一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》(基础篇)、(进阶篇)、(架构篇)清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…...
git的进阶使用
一.协作冲突 举个简单的例子,公司里两个人(A,B)同一天上班,都拉取了远程仓库数据。然后A做完了所有的工作,进行了x文件的修改并提交至远程仓库。而B在做自己工作的时候不小心或者需要修改x文件,B认为A没有操作x文件直接push没有问…...
23种设计模式-责任链(Chain of Responsibility)设计模式
责任链设计模式 🚩什么是责任链设计模式?🚩责任链设计模式的特点🚩责任链设计模式的结构🚩责任链设计模式的优缺点🚩责任链设计模式的Java实现🚩代码总结🚩总结 🚩什么是…...
MySQL复习
1基本操作复习 1.1数据库创建 创建数据库create database 数据库名;判断再创建数据库create database if not exists 数据库名;创建数据库指定字符集create database 数据库名 character set 字符集;创建数据库指定排序方式create database 数据库名 collate 排序方式;创建数据…...
【嵌入式学习2】c语言重点整理
目录 ## 重点掌握 1、数组 2、指针 3、结构体 4、函数 回调函数的常见用途 ## 如何区分数组指针,指针数组,函数指针,结构体指针,指针偏移量 ## 重点掌握 1、数组 https://blog.csdn.net/weixin_60546365/article/details…...
java项目之基于ssm的个人博客网站(源码+文档)
项目简介 个人博客网站实现了以下功能: 个人博客网站在Eclipse环境中,使用Java语言进行编码,使用Mysql创建数据表保存本系统产生的数据。系统可以提供信息显示和相应服务,其管理员审核博客文章和相册分享信息,管理文…...
C++学习之路:从头搞懂配置VScode开发环境的逻辑与步骤
目录 编辑器与IDE基于vscode的C开发环境配置1. 下载vscode、浅尝编译。番外篇 2. 安装插件,赋能编程。3. 各种json文件的作用。c_cpp_properties.jsontask.jsonlaunch.json 总结&&彩蛋 编辑器与IDE 上一篇博客已经介绍过了C程序的一个编译流程,从…...
deploy myEclipse j2ee project to server没反应
解决办法 1.如果工作空间的问题,那么需要删除你工作空间的一个文件就可以解决了。 这个文件在Myeclipse工作区(workspace) .metadata\.plugins\org.eclipse.core.runtime\.settings目录...
react项目中当组件渲染的时候如何执行接口
最近遇到一个场景,就是组件渲染的时候去调用接口进行数据回填。这个在vue中很简单,在created生命周期函数中,直接调用接口即可。但是react没有created生命周期,所以在react中我们需要用到useEffect钩子函数。 在 React 函数组件中…...
python虚拟环境安装opus(windows)
python -m venv venv 创建虚拟环境后,并且安装软件包后,运行项目报错,提示如下: Could not find Opus library. Make sure it is installed 原因是缺少opus.dll, (先把项目内所有使用的第三方库都安装完成) 从以下页面下载.dll文件之后,放入venv\Scripts目录下即可 https://…...
手机怎么换网络IP有什么用?操作指南与场景应用
在数字化时代,手机已经成为我们日常生活中不可或缺的一部分,无论是工作、学习还是娱乐,手机都扮演着至关重要的角色。而在手机的使用过程中,网络IP地址作为设备在互联网上的唯一标识符,其重要性和作用不容忽视。本文将…...
小程序内表格合并功能实现—行合并
功能介绍:支付宝小程序手写表格实现行内合并,依据动态数据自动计算每次需求合并的值,本次记录行内合并,如果列内合并,同理即可实现 前端技术:grid布局 display:grid 先看实现效果: axml&…...
基于Flask的通用登录注册模块,并代理跳转到目标网址
实现了用户密码的加密,代理跳转到目标网址,不会暴露目标路径,未登录的情况下访问proxy则自动跳转到登录页,使用时需要修改配置项config,登录注册页面背景快速修改,可以实现登录注册模块的快速复用。 1.app…...
nlohmann::json教程
nlohmann::json 核心函数和方法 1. 基础构造与初始化 函数/方法描述示例json j;创建一个空的 JSON 对象(默认是 object 类型)json j;json::object()显式创建一个空的 JSON 对象json j json::object();json::array()显式创建一个空的 JSON 数组json ar…...
多层感知机从0开始实现
《动手学深度学习》-4.2-笔记 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数。 import torch from torch import nn from d2l import torch as d2lbatch_size …...
在K8S中使用ArgoCD做持续部署
一、了解argocd ArgoCD是一个基于Kubernetes的GitOps持续交付工具,应用的部署和更新都可以在Git仓库上同步实现,并自带一个可视化界面。本文介绍如何使用GitArgocd方式来实现在k8s中部署和更新应用服务。关于ci这一块这里不多介绍。主要讲解argocd如何实…...
Python中数据结构元组详解
在Python中,元组(Tuple)是一种不可变的序列类型,常用于存储一组有序的数据。与列表(List)不同,元组一旦创建,其内容无法修改。本文将详细介绍元组的基本操作、常见运算、内置函数以及…...
23种设计模式-命令(Command)设计模式
命令设计模式 🚩什么是命令设计模式?🚩命令设计模式的特点🚩命令设计模式的结构🚩命令设计模式的优缺点🚩命令设计模式的Java实现🚩代码总结🚩总结 🚩什么是命令设计模式…...
计算机网络——数据链路层的功能
目录 物理链路 逻辑链路 封装成帧(组帧) 帧定界 透明传输 SDU 差错控制 可靠传输 流量控制 介质访问控制 主机需要实现第一层到第五层的功能,而路由器这种节点只需要实现第一层到第三层的这些功能 假设左边用户需要给右边用户发送…...
Axure项目实战:智慧城市APP(一)首页(动态面板、拖动效果)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:智慧城市APP 主要内容:首页、政务公告、公交查询页面设计 应用场景:各类政务型、B端APP均可参考 案例展示:&am…...
Unity网络开发快速回顾
知识点来源:总结人间自有韬哥在, 唐老狮,豆包 目录 1.网络通信-通信必备知识-IP地址和端口类2.网络通信中序列化和反序列化2进制数据3.Socket类4.TCP同步服务端和客户端基础实现4.1.服务端基本实现4.2.客户端实现: 5.区分消息类型…...
鸿蒙学习笔记(1)-文件解读、编写程序、生命周期
一、文件解读 .hvigor:装有一些编译过程中的依赖缓存。 .idea:工具自动生成的,标记我们的工具是基于idea。 AppScope:代表着整个APP的配置,最后打包使用。之中的resources目录下是应用的名称和图片存放路径,其中app.json5: bund…...
多维动态规划 力扣hot100热门面试算法题 面试基础 核心思路 背题
多维动态规划 不同路径 https://leetcode.cn/problems/unique-paths/ 核心思路 比较简单 f[i][j] f[i - 1][j] f[i][j - 1] ; 示例代码 class Solution {public int uniquePaths(int n, int m) {int[][] f new int[n][m];for (int i 0; i < n; i)f[i][0] 1;for…...
C++ 多线程简要讲解
std::thread是 C11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。 一.构造函数 官网的构造函数如下: 1.默认构造函数和线程创建 thread() noexcept; 作用:创建一个 std::thread 对象,但…...
乐仓VUE常用点
页面跳转 发送 router.push({ name: config.editRouteName, query: { type: create, from: route.name as string, }, }); router.push({ name: MbdCalScheme }); 接收 const route useRoute(); const type compu…...
单一主数据系统 vs. 统一主数据中心,哪种更优?
在企业中,主数据管理(MDM)已成为确保数据一致性和高效运营的关键。企业通常使用多个系统来处理业务数据,如ERP、CRM、SRM等。在这种多系统环境下,如何统一管理企业数据、避免数据孤岛,成为了一项重大挑战。…...
数据结构—树(java实现)
目录 一、树的基本概念1.树的术语2.常见的树结构 二、节点的定义三、有关树结构的操作1.按照数组构造平衡 二叉搜索树2.层序遍历树3.前、中、后序遍历树(1).前序遍历树(2).中序遍历树(3).后序遍历树(4).各种遍历的情况的效果对比 4.元素添加5.元素删除1.删除叶子节点2.删除单一…...
Modbus RTU ---> Modbus TCP透传技术实现(Modbus透传、RS485透传、RTU透传)分站代码实现、协议转换器
文章目录 Modbus RTU到Modbus TCP透传技术实现1. 透传技术概述1.1 透传基本原理- 协议帧格式转换- 地址映射与管理- 通信时序适配- 错误检测与处理 2. 透传网关硬件架构2.1 典型硬件结构- 微控制器/处理器(ARM、STM32等)- RS-485/RS-232收发器- 以太网控制器(如W5500)- 电源管理…...
Flask(三)路由与视图函数
在 Flask 中,路由 (Route) 是将 URL 地址映射到特定的视图函数 (View Function) 的机制。视图函数处理用户请求,并返回 HTTP 响应。理解路由和视图函数是构建 Flask 应用的基础。 3.1 路由的基本概念 Flask 使用 app.route() 装饰器来定义路由。以下是…...