设计模式|发布-订阅模式(Publish-Subscribe Pattern)
文章目录
- 初识发布-订阅模式
- 发布-订阅模式的关键概念
- 发布订阅模式的优缺点
- 示例代码(使用 Java 实现)
- 有哪些知名框架使用了发布-订阅模式
- 常见面试题
初识发布-订阅模式
发布-订阅模式(Publish-Subscribe Pattern)是一种软件架构设计模式,属于行为型设计模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。当有新消息发布时,订阅者将收到通知并执行相应的操作。
发布-订阅模式的关键概念
- 发布者(Publisher):负责发布消息的组件。它们通常不知道谁会接收到消息,只是将消息发送给与之连接的消息队列或主题。
- 订阅者(Subscriber):订阅特定类型的消息,并在该类型的消息被发布时接收到通知。订阅者可以根据自己的需求选择订阅的消息类型。
- 消息(Message):由发布者发布并由订阅者接收的信息单元。消息可以是任何形式的数据,例如文本、JSON、XML等。
- 主题(Topic):定义消息类型的逻辑通道或分类。发布者将消息发布到特定的主题,而订阅者则根据需要订阅特定的主题。
- 消息队列(Message Queue):用于在发布者和订阅者之间传递消息的中介服务。它可以确保消息的异步传输,并提供缓冲和路由消息的功能。
- 事件总线(Event Bus):类似于消息队列,用于在组件之间传递消息,但通常更为轻量级,通常在单个应用程序内部使用。
发布订阅模式的优缺点
发布-订阅模式(Publish-Subscribe Pattern)具有许多优点和一些缺点:
优点:
- 解耦性(Decoupling): 发布-订阅模式实现了生产者和消费者之间的解耦,发布者和订阅者之间的通信通过中介(例如消息队列、事件总线)进行,彼此不直接依赖或知晓对方的存在,从而提高了系统的灵活性和可维护性。
- 扩展性(Scalability): 由于发布者和订阅者之间的解耦,系统可以更容易地扩展。新的发布者或订阅者可以被添加而不影响现有的组件。
- 灵活性(Flexibility): 发布-订阅模式允许任意数量的发布者和订阅者存在,并且支持多对多的通信。发布者和订阅者可以根据需求动态地添加、删除或修改,而不影响整个系统的运行。
- 异步通信(Asynchronous Communication): 由于发布者和订阅者之间的通信通常是通过消息队列或事件总线进行的,因此支持异步通信。这使得系统能够更高效地处理大量消息,并提高了响应性。
- 松散耦合(Loose Coupling): 发布-订阅模式降低了组件之间的耦合度,因为它们不需要直接知道彼此的存在或实现细节。这使得系统更容易理解、维护和扩展。
缺点:
- 消息传递顺序性难以保证(Ordering of Message Delivery): 在某些情况下,由于消息传递是异步的,发布者发布消息的顺序与订阅者接收消息的顺序可能会不一致。这可能导致一些潜在的问题,特别是对于依赖于消息顺序的场景。
- 调试复杂性(Debugging Complexity): 由于发布-订阅模式中的组件之间是松散耦合的,因此在调试时可能会更加复杂。当出现问题时,需要跟踪消息的传递路径以找到问题所在。
- 消息处理延迟(Message Processing Latency): 由于发布-订阅模式通常是异步的,消息的传递和处理可能会引入一定程度的延迟。在某些实时性要求高的应用场景中,这可能会成为一个问题。
- 可能引入过多的订阅者(Potential Overuse of Subscribers): 如果不加限制地使用发布-订阅模式,可能会导致系统中存在过多的订阅者,这可能会降低系统的性能和可维护性。因此,需要在设计时仔细考虑订阅者的数量和范围。
虽然发布-订阅模式具有一些缺点,但它的优点通常能够满足许多实际应用场景的需求,并且在大多数情况下,其优势远远超过了缺点。因此,在选择使用发布-订阅模式时,需要根据具体的需求和场景来权衡利弊。
示例代码(使用 Java 实现)
下面是一个简单的 Java 实现发布-订阅模式的例子:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// 定义事件类
class Event {private String message;public Event(String message) {this.message = message;}public String getMessage() {return message;}
}// 定义发布者类
class Publisher {private Map<String, List<Subscriber>> subscribers = new HashMap<>();// 订阅public void subscribe(String eventType, Subscriber subscriber) {subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(subscriber);}// 发布消息public void publish(String eventType, Event event) {List<Subscriber> subscribersList = subscribers.getOrDefault(eventType, new ArrayList<>());for (Subscriber subscriber : subscribersList) {subscriber.notify(event);}}
}// 定义订阅者接口
interface Subscriber {void notify(Event event);
}// 定义具体的订阅者类
class ConcreteSubscriber implements Subscriber {private String name;public ConcreteSubscriber(String name) {this.name = name;}@Overridepublic void notify(Event event) {System.out.println(name + " received message: " + event.getMessage());}
}public class PublishSubscribeExample {public static void main(String[] args) {Publisher publisher = new Publisher();// 创建两个订阅者Subscriber subscriber1 = new ConcreteSubscriber("Subscriber 1");Subscriber subscriber2 = new ConcreteSubscriber("Subscriber 2");// 订阅事件类型为 "news"publisher.subscribe("news", subscriber1);publisher.subscribe("news", subscriber2);// 发布事件类型为 "news" 的消息publisher.publish("news", new Event("Breaking news: COVID restrictions lifted!"));}
}
在这个例子中,首先定义了 Event 类来表示事件,然后定义了 Publisher 类作为发布者,它维护了一个映射,将事件类型与订阅者列表关联起来。Publisher 类具有 subscribe 方法用于订阅特定类型的事件,以及 publish 方法用于发布事件。
然后,定义了 Subscriber 接口,其中包含一个 notify 方法,用于在订阅者接收到消息时进行通知。ConcreteSubscriber 类实现了 Subscriber 接口,并实现了 notify 方法来处理接收到的消息。
最后,在 main 方法中,创建了一个 Publisher 对象,两个 ConcreteSubscriber 对象,并将它们订阅了事件类型为 “news” 的消息。然后,发布了一个事件类型为 “news” 的消息,所有订阅了该事件类型的订阅者都会收到通知并处理该消息。
有哪些知名框架使用了发布-订阅模式
许多知名的框架和库都采用了发布-订阅模式或者类似的事件驱动机制来解耦组件之间的通信。以下是一些常见的采用发布-订阅模式的知名框架和库:
- Apache Kafka:Apache Kafka 是一个分布式的流处理平台和消息队列系统,它采用了发布-订阅模式来支持大规模的消息传递。
- RabbitMQ:RabbitMQ 是一个开源的消息队列系统,它支持多种消息传递模式,其中包括发布-订阅模式。
- Redis:Redis 是一个开源的内存数据库,它提供了发布-订阅功能,允许客户端订阅多个频道或模式,并在消息发布到这些频道或模式时接收通知。
- Spring Framework:Spring Framework 提供了一个事件机制,通过 ApplicationEvent 和 ApplicationListener 接口,以及 @EventListener 注解,可以实现发布-订阅模式来处理应用程序中的事件。
- RxJava:RxJava 是一个基于观察者模式的响应式编程库,它提供了丰富的操作符和组合器,用于处理异步事件流。
- Vert.x:Vert.x 是一个用于构建响应式和事件驱动应用程序的工具包,它采用了发布-订阅模式来处理异步消息和事件。
- EventEmitter in Node.js:Node.js 中的 EventEmitter 是一个核心模块,用于实现发布-订阅模式,允许对象触发命名事件,并允许注册事件的监听器来处理这些事件。
这些框架和库都利用了发布-订阅模式的优点,如解耦、灵活性和可扩展性,从而更好地支持异步消息传递和事件处理。
常见面试题
在面试中,关于发布-订阅模式可能会涉及到多个方面的问题,包括基本概念、优点、实际应用和实现细节等。以下是一些可能会遇到的问题以及相应的答案:
- 发布-订阅模式的基本概念是什么?
答案:发布-订阅模式是一种软件架构模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。 - 发布-订阅模式与观察者模式有何区别?
答案:发布-订阅模式和观察者模式都用于处理对象之间的通信,但它们之间有一些区别。观察者模式中,主题(被观察者)维护了一组观察者对象,并在状态发生变化时通知它们。而在发布-订阅模式中,发布者和订阅者之间没有直接的关联,发布者将消息发布到特定的主题,而订阅者可以选择订阅他们感兴趣的主题,从而解耦了生产者和消费者。 - 发布-订阅模式的优点是什么?
答案:发布-订阅模式具有以下优点:
- 解耦性:发布者和订阅者之间没有直接的依赖关系,从而实现了解耦。
- 灵活性:发布者和订阅者可以独立地进行扩展和修改,而不会影响到对方。
- 可扩展性:新的发布者和订阅者可以很容易地加入到系统中,而不需要修改现有的代码。
- 多对多通信:一个发布者可以有多个订阅者,一个订阅者也可以订阅多个发布者,实现了多对多的通信。
- 请举例说明一个实际应用场景中使用发布-订阅模式的情况。
答案:一个实际应用场景是在线社交平台的消息推送功能。例如,社交平台上的用户可以选择订阅他们感兴趣的话题或其他用户的动态,而发布者则负责将新的消息发布到相应的主题上。这样一来,用户就可以接收到他们感兴趣的消息,而发布者和订阅者之间的关系是解耦的。 - 在实现发布-订阅模式时,有哪些关键的组件?
答案:实现发布-订阅模式时,关键的组件包括发布者(负责发布消息)、订阅者(订阅感兴趣的消息)、消息(发布者发布的信息单元)、主题(定义消息类型的逻辑通道)、消息队列或事件总线(用于在发布者和订阅者之间传递消息的中介服务)等。 - 请解释发布-订阅模式的原理。
答案: 发布-订阅模式是一种软件架构设计模式,用于解耦生产者(发布者)和消费者(订阅者)之间的关系。在这种模式中,发布者负责发布消息,而订阅者则可以选择订阅他们感兴趣的消息类型。当有新消息发布时,订阅者将收到通知并执行相应的操作,从而实现了组件之间的解耦和松耦合。
这些问题可以帮助面试官评估候选人对发布-订阅模式的理解程度,以及其在实际应用中的应用能力。
相关文章:
设计模式|发布-订阅模式(Publish-Subscribe Pattern)
文章目录 初识发布-订阅模式发布-订阅模式的关键概念发布订阅模式的优缺点示例代码(使用 Java 实现)有哪些知名框架使用了发布-订阅模式常见面试题 初识发布-订阅模式 发布-订阅模式(Publish-Subscribe Pattern)是一种软件架构设…...
根据疾病名生成病例prompt
prompt 请根据疾病名:" disease_name " 为我生成一份病历。下面是病历内容的要求:病例应严格包含如下几项: 性别,年龄,疾病名(必须是" disease_name "),主诉ÿ…...
HarmonyOS网格布局:List组件和Grid组件的使用
简介 在我们常用的手机应用中,经常会见到一些数据列表,如设置页面、通讯录、商品列表等。下图中两个页面都包含列表,“首页”页面中包含两个网格布局,“商城”页面中包含一个商品列表。 上图中的列表中都包含一系列相同宽度的列表…...
NASA数据集—— 1984-2019年湖泊生长季绿色表面反射率趋势数据集
ABoVE: Lake Growing Season Green Surface Reflectance Trends, AK and Canada, 1984-2019 简介 该数据集提供了1984年至2019年期间ABoVE扩展研究域内472,890个湖泊的大地遥感卫星绿色表面反射率年度时间序列和衍生的年度生长季节(6月和7月)趋势。反射…...
DMA知识
提示:文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问: 本文目标: 一、背景 2024年3月26日23:32:43 今天看了DMA存储器到存储器的DMA传输和存储器到外设的DMA实验,在keil仿真可以看到效果。还没有在protues和开发…...
Linux 系统 docker快速搭建PHP环境
PHP安装 ############################################################################# 1、直接拉取官方镜像 查找Docker Hub上的php镜像 docker search php 直接拉取官方镜像 docker run --name myphp --restartalways --network lnmp -d php:7.1-fpm 2、创建php容…...
逻辑设计问题 -- 设计一个函数
文章目录 设计一个函数函数接口规格说明运算符或者非运算符自由或成员运算符虚函数或非虚函数纯虚函数或者非纯虚函数静态或者非静态成员函数const 成员函数或者非const成员函数公共的、保护的或者私有的成员函数通过值、引用或者指针返回返回const 或者非const可选参数或者必要…...
RHCE 补充:判断服务状态
内容补充:判断服务状态 systemctl 命令 系统控制管理命令工具 常用指令 1、启动 systemctl start 程序名 若要启动多个程序名,使用空格隔开,下同 2、重启:类似主机先断电再启动的一个状态 systemctl restart 程序名 3、停…...
计算机网络:物理层 - 编码与调制
计算机网络:物理层 - 编码与调制 基本概念编码不归零制编码归零制编码曼彻斯特编码差分曼彻斯特编码 调制调幅调频调相混合调制 基本概念 在计算机网络中,计算机需要处理和传输用户的文字、图片、音频和视频,他们可以统称为消息数据…...
《量子计算:揭开未来科技新篇章》
随着科技的不断发展,量子计算作为一项颠覆性的技术逐渐走进人们的视野,引发了广泛的关注和探讨。本文将围绕量子计算的技术进展、技术原理、行业应用案例、未来趋势预测以及学习路线等方向,深入探讨这一领域的前沿动态和未来发展趋势。 量子…...
机器人机械手加装SycoTec 4060 ER-S电主轴高精密铣削加工
随着科技的不断发展,机器人技术正逐渐渗透到各个领域,展现出前所未有的潜力和应用价值。作为机器人技术的核心组成部分之一,机器人机械手以其高精度、高效率和高稳定性的优势,在机械加工、装配、检测等领域中发挥着举足轻重的作用…...
docker 共享内存不足问题
在启动容器时增加共享内存大小: 您可以通过在docker run命令中添加--shm-size参数来指定更大的共享内存大小。例如,如果您需要32GB的共享内存,可以这样做: docker run --shm-size32g -it your-docker-image 这里的your-docker-im…...
英语口语 3.27
keep It straight :竖着放 turn it to the side:横过来放 i get my shit done:shit(everything)任何事情 我都会去做的 that‘s what’s up 可以的可以的 thats cool zodiac sign :生肖 座 i sense that :我感受到了 talent”艺人 influencer:有影响力的人 …...
pytest之统一接口请求封装
pytest之统一接口请求封装 pytest的requests_util.pyrequests_util.py 接口自动化测试框架的封装yaml文件如何实现接口关联封装yaml文件如何实现动态参数的处理yaml文件如何实现文件上传有参数化时候,怎么实现断言yaml的数据量大怎么处理接口自动化框架的扩展&#…...
使用npm仓库的优先级以及.npmrc配置文件的使用
使用npm仓库的优先级以及.npmrc配置文件的使用 概念如何设置 registry(包管理仓库)1. 设置项目配置文件2. 设置用户配置文件3. 设置全局配置文件4. .npmrc文件可以配置的常见选项 概念 npm(Node Package Manager)是一个Node.js的…...
Netty源码剖析——ChannelHandlerContext 篇(三十七)
ChannelHandlerContext 作用及设计 ChannelHandlerContext 继承了出站方法调用接口和入站方法调用接口 ChannelOutboundInvoker 和 ChannelInboundInvoker 部分源码 这两个invoker就是针对入站或出站方法来的,就是在入站或出站 handler 的外层再包装一层,…...
5.92 BCC工具之bitesize.py解读
一,工具简介 bitesize工具按进程名称显示请求块大小的I/O分布。 它通过监视磁盘上的读取和写入操作,记录每个操作的大小。再将跟踪到的 I/O 操作按照大小分组,通常是以 2 的幂次方(如 4K、8K、16K 等)进行划分,并统计每个大小范围内的 I/O 操作数量。 二,代码示例 #…...
jupyter notebook导出含中文的pdf(LaTex安装和Pandoc、MiKTex安装)
用jupyter notebook导出pdf时,因为报错信息,需要用到Tex nbconvert failed: xelatex not found on PATH, if you have not installed xelatex you may need to do so. Find further instructions at https://nbconvert.readthedocs.io/en/latest/install…...
压力测试(QPS)及测试工具Locust
压力测试: 通常指的是确定接口或服务能够处理的最大请求量(吞吐量)和并发用户数,同时保持合理的响应时间和稳定性。 性能目标 最大吞吐量:系统每秒可以处理的请求数。最大并发用户数:系统可以同时支持的…...
canal: 连接kafka (docker)
一、确保mysql binlog开启并使用ROW作为日志格式 docker 启动mysql 5.7配置文件 my.cnf [mysqld] log-binmysql-bin # 开启 binlog binlog-formatROW # 选择 ROW 模式 server-id1一定要确保上述两个值一个为ROW,一个为ON 二、下载canal的run.sh https://github.c…...
CHORD-X深度研究报告生成:集成MySQL进行数据存储与管理的配置指南
CHORD-X深度研究报告生成:集成MySQL进行数据存储与管理的配置指南 如果你正在使用CHORD-X这类强大的研究报告生成工具,可能会遇到一个甜蜜的烦恼:生成的内容越来越多,数据越来越杂,怎么才能把它们管得井井有条&#x…...
【手把手教学】Tesseract-OCR图片文字识别从安装到实战
1. Tesseract-OCR简介与安装准备 第一次接触图片文字识别技术时,我和很多人一样觉得这是个遥不可及的黑科技。直到发现了Tesseract-OCR这个开源工具,才发现原来OCR技术可以如此亲民。Tesseract最初由HP实验室开发,后来由Google接手维护&#…...
慕尼黑工业大学全新突破:让2D图片生成器变身3D世界建造师
这项由慕尼黑工业大学领导的研究发表于2026年的计算机视觉与模式识别顶级会议,论文编号为arXiv:2603.19708v1。有兴趣深入了解的读者可以通过该编号查询完整论文。当你使用手机拍摄一张美丽风景照片时,你可能从未想过,这张平面照片其实包含了…...
避开高光谱求导的坑:你的平滑做对了吗?附MATLAB代码与数据示例
高光谱微分预处理实战指南:如何避免噪声放大陷阱 第一次处理高光谱数据时,我兴奋地直接对原始光谱曲线求导,结果得到了一堆杂乱无章的噪声信号。这个教训让我明白了一个关键原则:未经平滑的微分操作就像在放大镜下观察指纹——细节…...
为什么conda装不上opencv-python?深入解析conda与pip的包管理差异
为什么conda装不上opencv-python?深入解析conda与pip的包管理差异 在Python生态系统中,conda和pip是最常用的两种包管理工具。许多开发者习惯使用conda创建和管理虚拟环境,但在安装某些特定包如opencv-python时,却常常遇到"P…...
查询参数拼接
export function objectToQueryString(params) {return Object.entries(params).filter(([key, value]) > value ! undefined && value ! null) // 过滤掉 undefined 和 null.map(([key, value]) > ${encodeURIComponent(key)}${encodeURIComponent(value)}).joi…...
ChatGPT在代码安全实战中的5个隐藏技巧:从漏洞检测到恶意软件分析
ChatGPT在代码安全实战中的5个隐藏技巧:从漏洞检测到恶意软件分析 当开发者第一次听说ChatGPT能帮忙写代码时,大多数人想到的可能是自动补全函数或生成简单脚本。但很少有人意识到,这个看似普通的对话AI,正在成为代码安全领域的&q…...
Polars 2.0大规模清洗性能翻倍的7个底层优化技巧:基于真实金融风控流水线压测数据
第一章:Polars 2.0大规模数据清洗性能跃迁的工程意义Polars 2.0 的发布标志着 Rust 原生 DataFrame 库在工程落地层面实现关键突破——其基于 Arrow 2.0 和全新查询优化器(QOv2)重构的执行引擎,将典型 ETL 清洗任务的吞吐量提升达…...
COMSOL激光烧蚀激光融覆选区激光融化 激光直接沉积过程中,快速熔化凝固和多组分粉末的加入导...
COMSOL激光烧蚀激光融覆选区激光融化 激光直接沉积过程中,快速熔化凝固和多组分粉末的加入导致了熔池中复杂的输运现象。 热行为对凝固组织和性能有显著影响。 通过三维数值模型来模拟在316L上直接激光沉积过程中的传热、流体流动、凝固过程。 通过瞬态热分布可以获…...
前端 跨域解决方案
一、什么是跨域? 协议、域名、端口 三者有任意一个不一样,就是跨域。 浏览器出于安全考虑,会限制跨域请求,这就是同源策略(Same-Origin Policy)。 举例: https://www.baidu.com协议:…...
