JAVA设计模式之职责链模式详解
职责链模式
1 职责链模式介绍
职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止.
在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请 求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再 传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职 责,所以叫作职责链模式。
2 职责链模式原理
职责链模式结构
职责链模式主要包含以下角色:
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接(链上的每个处理者都有一个成员变量来保存对于下一处理者的引用,比如上图中的successor) 。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
3 职责链模式实现
责任链模式的实现非常简单,每一个具体的处理类都会保存在它之后的下一个处理类。当处理完成后,就会调用设置好的下一个处理类,直到最后一个处理类不再设置下一个处理类,这时处理链条全部完成。
public class RequestData {private String data;public RequestData(String data) {this.data = data;}public String getData() {return data;}public void setData(String data) {this.data = data;}
}/*** 抽象处理者类**/
public abstract class Handler {protected Handler successor = null;public void setSuccessor(Handler successor){this.successor = successor;}public abstract void handle(RequestData requestData);
}public class HandlerA extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerA 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("A",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class HandlerB extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerB 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData().replace("B",""));if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class HandlerC extends Handler {@Overridepublic void handle(RequestData requestData) {System.out.println("HandlerC 执行代码逻辑! 处理: " + requestData.getData());requestData.setData(requestData.getData());if(successor != null){successor.handle(requestData);}else{System.out.println("执行中止!");}}
}public class Client {public static void main(String[] args) {Handler h1 = new HandlerA();Handler h2 = new HandlerB();Handler h3 = new HandlerC();h1.setSuccessor(h2);h2.setSuccessor(h3);RequestData requestData = new RequestData("请求数据ABCDE");h1.handle(requestData);}}
4 职责链模式应用实例
接下来我们模拟有一个双11期间,业务系统审批的流程,临近双十一公司会有陆续有一些新的需求上线,为了保证线上系统的稳定,我们对上线的审批流畅做了严格的控制.审批的过程会有不同级别的负责人加入进行审批(平常系统上线只需三级负责人审批即可,双十一前后需要二级或一级审核人参与审批),接下来我们就使用职责链模式来设计一下此功能.
- 不使用设计模式
/*** 审核信息**/
public class AuthInfo {private String code;private String info ="";public AuthInfo(String code, String... infos) {this.code = code;for (String str : infos) {info = this.info.concat(str +" ");}}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}@Overridepublic String toString() {return "AuthInfo{" +"code='" + code + '\'' +", info='" + info + '\'' +'}';}
}/*** 模拟审核服务**/
public class AuthService {//审批信息 审批人Id+申请单Idprivate static Map<String,Date> authMap = new HashMap<String, Date>();/*** 审核流程* @param uId 审核人id* @param orderId 审核单id*/public static void auth(String uId, String orderId){System.out.println("进入审批流程,审批人ID: " + uId);authMap.put(uId.concat(orderId),new Date());}//查询审核结果public static Date queryAuthInfo(String uId, String orderId){return authMap.get(uId.concat(orderId)); //key=审核人id+审核单子id}
}public class AuthController {//审核接口public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {//三级审批Date date = null;//查询是否存在审核信息,查询条件: 审核人ID+订单ID,返回Map集合中的Datedate = AuthService.queryAuthInfo("1000013", orderId);//如果为空,封装AuthInfo信息(待审核)返回if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待三级审批负责人进行审批");}//二级审批SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化//二级审核人主要审核双十一之前, 11-01 ~ 11-10号的请求,所以要对传入的审核时间进行判断//审核时间 大于 2022-11-01 并且 小于 2022-11-10,Date1.after(Date2),当Date1大于Date2时,返回TRUE,Date1.before(Date2),当Date1小于Date2时,返回TRUEif(authDate.after(f.parse("2022-11-01 00:00:00")) && authDate.before(f.parse("2022-11-10 00:00:00"))){//条件成立,查询二级审核的审核信息date = AuthService.queryAuthInfo("1000012",orderId);//如果为空,还是待二级审核人审核状态if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待二级审批负责人进行审批");}}//一级审批//审核范围是在11-11日 ~ 11-31日if(authDate.after(f.parse("2022-11-11 00:00:00")) && authDate.before(f.parse("2022-11-31 00:00:00"))){date = AuthService.queryAuthInfo("1000011",orderId);if(date == null){return new AuthInfo("0001","单号: "+orderId,"状态: 等待一级审批负责人进行审批");}}return new AuthInfo("0001","单号: "+orderId,"申请人:"+ name +", 状态: 审批完成!");}
}public class Client {public static void main(String[] args) throws ParseException {AuthController controller = new AuthController();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse("2022-11-12 00:00:00");//设置申请流程//三级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info1 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态: " + info1.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000013* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000013", "100001000010000");System.out.println("三级负责人审批完成,审批人: 王工");System.out.println("===========================================================================");//二级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info2 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态: " + info2.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000012", "100001000010000");System.out.println("二级负责人审批完成,审批人: 张经理");System.out.println("===========================================================================");//一级审核//1.调用doAuth方法,模拟发送申请人相关信息AuthInfo info3 = controller.doAuth("研发小周", "100001000010000", date);System.out.println("当前审核状态: " + info3.getInfo());/*** 2.模拟进行审核操作, 虚拟审核人ID: 1000012* 调用auth() 方法进行审核操作, 就是向Map中添加一个 审核人ID和申请单ID*/AuthService.auth("1000011", "100001000010000");System.out.println("一级负责人审批完成,审批人: 罗总");}
}
- 职责链模式重构代码
下图是为当前业务设计的责任链结构,统一抽象类AuthLink 下 有三个子类,将三个子类的执行通过编排,模拟出一条链路,这个链路就是业务中的责任链.
/*** 抽象审核链类*/
public abstract class AuthLink {protected Logger logger = LoggerFactory.getLogger(AuthLink.class);protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");protected String levelUserId; //审核人IDprotected String levelUserName; //审核人姓名protected AuthLink next; //持有下一个处理类的引用public AuthLink(String levelUserId, String levelUserName) {this.levelUserId = levelUserId;this.levelUserName = levelUserName;}//获取下一个处理类public AuthLink getNext() {return next;}//责任链中添加处理类public AuthLink appendNext(AuthLink next) {this.next = next;return this;}//抽象审核方法public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}/** 一级负责人*/
public class Level1AuthLink extends AuthLink {private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}@Overridepublic AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate)) {return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}/*** 二级负责人*/
public class Level2AuthLink extends AuthLink {private Date beginDate = f.parse("2020-11-11 00:00:00");private Date endDate = f.parse("2020-11-31 23:59:59");public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}if (authDate.before(beginDate) || authDate.after(endDate) ) {return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}}/*** 三级负责人*/
public class Level3AuthLink extends AuthLink {public Level3AuthLink(String levelUserId, String levelUserName) {super(levelUserId, levelUserName);}public AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);}AuthLink next = super.getNext();if (null == next) {return new AuthInfo("0000", "单号:", orderId, " 状态:三级审批完成", " 时间:", f.format(date), " 审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}}
测试
public class Client {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test_AuthLink() throws ParseException {AuthLink authLink = new Level3AuthLink("1000013", "王工").appendNext(new Level2AuthLink("1000012", "张经理").appendNext(new Level1AuthLink("1000011", "段总")));SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date currentDate = f.parse("2020-11-18 23:49:46");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟三级负责人审批AuthService.auth("1000013", "1000998004813441");logger.info("测试结果:{}", "模拟三级负责人审批,王工");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟二级负责人审批AuthService.auth("1000012", "1000998004813441");logger.info("测试结果:{}", "模拟二级负责人审批,张经理");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));// 模拟一级负责人审批AuthService.auth("1000011", "1000998004813441");logger.info("测试结果:{}", "模拟一级负责人审批,段总");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("研发牛马", "1000998004813441", currentDate)));}
}
从上面的代码结果看,我们的责任链已经生效,按照责任链的结构一层一层审批.当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。并且每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
5 职责链模式总结
- 职责链模式的优点:
-
降低了对象之间的耦合度
该模式降低了请求发送者和接收者的耦合度。
-
增强了系统的可扩展性
可以根据需要增加新的请求处理类,满足开闭原则。
-
增强了给对象指派职责的灵活性
当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
-
责任链简化了对象之间的连接
一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
-
责任分担
每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
- 职责链模式的缺点:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
- 使用场景分析
责任链模式常见的使用场景有以下几种情况。
- 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程等。
- 不想让使用者知道具体的处理逻辑时。比如,做权限校验的登录拦截器。
- 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
- 职责链模式常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的过滤拦截功能.
相关文章:

JAVA设计模式之职责链模式详解
职责链模式 1 职责链模式介绍 职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止. 在职责链模式中,…...

CSP-201912-1-报数
CSP-201912-1-报数 知识点总结 整数转化为字符串#include <string> string str_num to_string(num);字符串中查找是否包含字符‘7’:str_num.find(7) 未找到返回-1找到返回返回该字符在字符串中的位置(即第一次出现的索引位置) #i…...

前后端分离好处多多,怕就怕分工不分人,哈哈
前后端分离倡导多年了,现在基本成为了开发的主流模式了,贝格前端工场承接的前端项目只要不考虑seo的,都采用前后端分离模式,这篇文章就来介绍一下前后端分离模式。 一、什么是前后端分离开发模式 前后端分离是一种软件开发的架构…...

机器学习:Softmax介绍及代码实现
Softmax原理 Softmax函数用于将分类结果归一化,形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z,我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果,具体计算公式为: 对于…...

python基于flask的网上订餐系统769b9-django+vue
课题主要分为两大模块:即管理员模块和用户模块,主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等; 如果用户想要交换信息,他们需要满足双方交换信息的需要。由于时间有限…...

jenkins 发布远程服务器并部署项目
安装参考另一个文章 配置maven 和 jdk 和 git 注意jdk的安装目录,是jenkins 安装所在服务器的jdk目录 注意maven的目录 是jenkins 安装所在服务器的maven目录 注意git的目录 是jenkins 安装所在服务器的 git 目录 安装 Publish Over SSH 插件 配置远程服务器 创…...

【数学建模】【2024年】【第40届】【MCM/ICM】【D题 五大湖的水位控制问题】【解题思路】
一、题目 (一) 赛题原文 2024 ICM Problem D: Great Lakes Water Problem Background The Great Lakes of the United States and Canada are the largest group of freshwater lakes in the world. The five lakes and connecting waterways const…...

【开源】JAVA+Vue+SpringBoot实现公司货物订单管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…...

###C语言程序设计-----C语言学习(12)#进制间转换,十进制,二进制,八进制,十六进制
前言:感谢您的关注哦,我会持续更新编程相关知识,愿您在这里有所收获。如果有任何问题,欢迎沟通交流!期待与您在学习编程的道路上共同进步。 计算机处理的所有信息都以二进制形式表示,即数据的存储和计算都采…...
锐捷设备常用命令
一、命令模式 命令行主要有用户模式、特权模式、全局模式、VLAN模式、接口模式、线程模式 switch> "用户模式"switch# "特权模式"switch(config) "全局模式"switch(conf…...
python:lxml 读目录.txt文件,用 xmltodict 转换为json数据,生成jstree所需的文件
请参阅:java : pdfbox 读取 PDF文件内书签 请注意:书的目录.txt 编码:UTF-8,推荐用 Notepad 转换编码。 pip install lxml ; lxml-5.1.0-cp310-cp310-win_amd64.whl (3.9 MB) pip install xmltodict ; lxml 读目录.txt文件&…...

【Spring】Spring 对 Ioc 的实现
一、Ioc 控制反转 控制反转是一种思想 控制反转是为了降低程序耦合度,提高程序扩展力,达到 OCP 原则,达到 DIP 原则 控制反转,反转的是什么? 将对象的创建权利交出去,交给第三方容器负责 将对象和对象之…...

QT学习文件操作类 QFile
(一)QFile QFile 是 Qt 框架中用于文件处理的一个类。它提供了读取和写入文件的功能,支持文本和二进制文件。QFile 继承自 QIODevice ,因此它可以像其他 IO 设备一样使用。 (1)主要功能 1. 文件读写…...
VOL_常用记录!!
目录 前端1.js如何获取当前时间(yy-MM-dd HH:MM:SS)2.http请求3.grid扩展js常用 后端1.待补充 前端 1.js如何获取当前时间(yy-MM-dd HH:MM:SS) getCurrentTime() {const now new Date();return ${now.getFullYear()}-${(now.getMonth() 1).toString().padStart(2, "0&…...

解决Typora导出HTML不显示图片
解决Typora导出HTML不显示图片 产生原因 Typora导出HTML不显示图片,可能时图片存放在我们的硬盘中。 我们可以将markdown中的图片转化为base64格式,嵌入到html中。 解决步骤 首先,下载 TyporaToBase64.jar 密码:45jq 其次,将…...

React Native开发iOS实战录
文章目录 背景环境准备主要工具xcode安装安装CocoaPods 基本步骤采用Expo go运行iOS模拟器运行安装在真机上测试发布到苹果商店 常见问题ruby3在macOS上编译失败import of module ‘glog.glog.log_severity’ appears within namespace ‘google’yarn网络问题pod安装失败unabl…...
C++局部变量与全局变量
在C中,可以为函数的参数指定默认值。这样做的好处是在调用函数时,如果没有提供对应的参数,那么将会使用默认值。 下面是一个求2个或3个数中最大数的函数的示例,其中使用了默认参数: #include <iostream> using…...

深入理解ES的倒排索引
目录 数据写入过程 词项字典 term dictionary 倒排表 posting list FOR算法 RBM算法 ArrayContainer BitMapContainer 词项索引 term index 在Elasticsearch中,倒排索引的设计无疑是惊为天人的,下面看下倒排索引的结构。 倒排索引分为词项索引【…...
HTML世界之第一重天
一、HTML 元素 注:HTML 文档由 HTML 元素定义。 1.HTML 元素 开始标签 * 元素内容 结束标签 * <p> 这是一个段落 </p> <a href"default.htm"> 这是一个链接 </a> <br> 换行 开始标签常被称为起始标签&…...
docker run报 docker: Error response from daemon: no command specified.
docker run报 docker: Error response from daemon: no command specified. 1. export出mysql的container为tar, 拷贝到另一台虚拟机, import该tar为image, docker run该image时报 docker: Error response from daemon: no command specified. 时间240211 export出mysql的con…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...

WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...

【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...