详解RabbitMQ工作模式之发布订阅模式

article/2025/8/11 23:17:13

目录

发布订阅模式

概念

概念介绍

特点和优势

应用场景

注意事项

代码案例

引入依赖

常量类

编写生产者代码

编写消费者1代码

运行代码


发布订阅模式

概念

RabbitMQ的发布订阅模式(Publish/Subscribe)是一种消息传递模式,它允许消息生产者(Publisher)将消息发送到交换机(Exchange),然后交换机根据路由规则将消息广播到一个或多个队列,最后由消费者(Subscriber)从队列中接收并处理消息。

图中X表⽰交换机, 在订阅模型中,多了⼀个Exchange⻆⾊, 过程略有变化。

概念介绍

Exchange: 交换机 (X).
作⽤: ⽣产者将消息发送到Exchange, 由交换机将消息按⼀定规则路由到⼀个或多个队列中(上图中⽣产者将消息投递到队列中, 实际上这个在RabbitMQ中不会发⽣. )
RabbitMQ交换机有四种类型: fanout,direct, topic, headers, 不同类型有着不同的路由策略. AMQP协议⾥还有另外两种类型, System和⾃定义, 此处不再描述.

1. Fanout:⼴播,将消息交给所有绑定到交换机的队列(Publish/Subscribe模式)
2. Direct:定向,把消息交给符合指定routing key的队列(Routing模式)
3. Topic:通配符,把消息交给符合routing pattern(路由模式)的队列(Topics模式)
4. headers类型的交换器不依赖于路由键的匹配规则来路由消息, ⽽是根据发送的消息内容中的headers属性进⾏匹配. headers类型的交换器性能会很差,⽽且也不实⽤,基本上不会看到它的存在.

Exchange(交换机)只负责转发消息, 不具备存储消息的能⼒, 因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息就会丢失
RoutingKey: 路由键.⽣产者将消息发给交换器时, 指定的⼀个字符串, ⽤来告诉交换机应该如何处理这个消息.
Binding Key:绑定. RabbitMQ中通过Binding(绑定)将交换器与队列关联起来, 在绑定的时候⼀般会指定⼀个Binding Key, 这样RabbitMQ就知道如何正确地将消息路由到队列了.

⽐如下图: 如果在发送消息时, 设置了RoutingKey 为orange, 消息就会路由到Q1

当消息的Routing key与队列绑定的Bindingkey相匹配时,消息才会被路由到这个队列.

BindingKey其实也属于路由键中的⼀种, 官⽅解释为:the routingkey to use for the binding.
可以翻译为:在绑定的时候使⽤的路由键. ⼤多数时候,包括官⽅⽂档和RabbitMQJava API 中都把
BindingKey和RoutingKey看作RoutingKey, 为了避免混淆,可以这么理解:

1. 在使⽤绑定的时候,需要的路由键是BindingKey.
2. 在发送消息的时候,需要的路由键是RoutingKey

特点和优势

1.解耦合:生产者和消费者之间通过交换机进行解耦。生产者无需知道消息将被传递到哪些队列,消费者也无需知道消息来自哪个生产者。这种解耦合使得系统更加灵活和可扩展。
2.多播:支持多个消费者同时处理同一条消息,实现消息的多播效果。这有助于提高系统的并行处理能力和容错性。
3.灵活性:可以根据需要使用不同类型的交换机和绑定规则,以满足不同的消息传递需求。RabbitMQ提供了多种交换机类型,如直接交换机、扇形交换机、主题交换机等。

应用场景

发布订阅模式适用于需要将消息广播给多个消费者的场景,例如:

1.实时通知:如系统状态更新、订单状态变更等实时事件的通知。通过发布订阅模式,可以将这些事件广播给所有感兴趣的消费者。
2.日志记录:将应用程序的日志信息广播到多个日志处理服务进行处理和存储。这有助于实现日志的集中管理和分析。
3.事件处理:在事件驱动架构中,将事件作为消息发布到交换机,由多个消费者订阅并处理这些事件。这有助于实现事件的异步处理和分布式处理。

注意事项

1.消息持久化:为了确保消息在RabbitMQ服务器重启后不会丢失,可以将消息和队列标记为持久性。这样,即使服务器发生故障,消息仍然可以被消费者接收和处理。
2.消息确认:RabbitMQ支持消息确认机制,确保消息在成功处理后才会从队列中删除。这有助于防止消息丢失和重复处理。
3.负载均衡:在发布订阅模式中,多个消费者可以监听同一个队列或不同的队列。为了实现负载均衡,可以配置多个消费者来处理同一个队列中的消息。

代码案例
引入依赖
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.21.0</version>
</dependency>
常量类
public class Constants {public static final String HOST = "47.98.109.138";public static final int PORT = 5672;public static final String USER_NAME = "study";public static final String PASSWORD = "study";public static final String VIRTUAL_HOST = "aaa";//发布订阅模式public static final String FANOUT_EXCHANGE = "fanout.exchange";public static final String FANOUT_QUEUE1 = "fanout.queue1";public static final String FANOUT_QUEUE2 = "fanout.queue2";
}
编写生产者代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {//1. 建立连接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT); //需要提前开放端口号connectionFactory.setUsername(Constants.USER_NAME);//账号connectionFactory.setPassword(Constants.PASSWORD);  //密码connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST); //虚拟主机Connection connection = connectionFactory.newConnection();//2. 开启信道Channel channel = connection.createChannel();//3. 声明交换机channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);//4. 声明队列channel.queueDeclare(Constants.FANOUT_QUEUE1,true,false,false,null);channel.queueDeclare(Constants.FANOUT_QUEUE2,true,false,false,null);//5. 交换机和队列绑定channel.queueBind(Constants.FANOUT_QUEUE1,Constants.FANOUT_EXCHANGE,"");channel.queueBind(Constants.FANOUT_QUEUE2,Constants.FANOUT_EXCHANGE,"");//6. 发布消息String msg = "hello fanout....";channel.basicPublish(Constants.FANOUT_EXCHANGE,"", null, msg.getBytes());System.out.println("消息发送成功");//7. 释放资源channel.close();connection.close();}
}
编写消费者1代码
import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer1 {public static void main(String[] args) throws IOException, TimeoutException {//1. 建立连接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT); //需要提前开放端口号connectionFactory.setUsername(Constants.USER_NAME);//账号connectionFactory.setPassword(Constants.PASSWORD);  //密码connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST); //虚拟主机Connection connection = connectionFactory.newConnection();//2. 开启信道Channel channel = connection.createChannel();//3. 声明队列channel.queueDeclare(Constants.FANOUT_QUEUE1,true,false,false,null);//4. 消费消息DefaultConsumer consumer = new DefaultConsumer(channel){//从队列中收到消息, 就会执行的方法@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("接收到消息:"+ new String(body));}};channel.basicConsume(Constants.FANOUT_QUEUE1, true, consumer);}
}

编写消费者2代码

import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer2 {public static void main(String[] args) throws IOException, TimeoutException {//1. 建立连接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT); //需要提前开放端口号connectionFactory.setUsername(Constants.USER_NAME);//账号connectionFactory.setPassword(Constants.PASSWORD);  //密码connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST); //虚拟主机Connection connection = connectionFactory.newConnection();//2. 开启信道Channel channel = connection.createChannel();//3. 声明队列channel.queueDeclare(Constants.FANOUT_QUEUE2,true,false,false,null);//4. 消费消息DefaultConsumer consumer = new DefaultConsumer(channel){//从队列中收到消息, 就会执行的方法@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("接收到消息:"+ new String(body));}};channel.basicConsume(Constants.FANOUT_QUEUE2, true, consumer);}
}
运行代码

运行生产者代码之后,可以在管理界面看到两个队列中都有1条消息。

由此我们可以看到,交换机收到的生产者生产的一条消息被广播到了两个队列,消费者都能够分别从这两个队列中得到一条消息并消费。 


http://www.hkcw.cn/article/NNniPukgbp.shtml

相关文章

Java操作RabbitMQ

文章目录 Spring集成RabbitMQ1. AMQP&SpringAMQP2. SpringBoot集成RabbitMQ3. 模型work模型 4.交换机Fanout交换机Direct交换机Topic交换机 5.声明式队列和交换机基于API声明基于注解声明 6.消息转换器 Spring集成RabbitMQ 1. AMQP&SpringAMQP AMQP&#xff08;高级消…

女龙队从佛山九江“扒”向世界 农村大妈划出国际范

临近端午,广东多地的河道里又开始热闹起来,特别是佛山地区的龙舟活动历史悠久。九江镇被誉为全国首个龙舟名镇,赛龙舟的传统已有300年历史。九江是典型的水乡,河网密布,几乎每个村子都有河流经过,划船出行是最便捷的交通方式。在村民眼中,龙舟不仅能聚人气,还能增强村里…

同济大学教授路秉杰逝世 建筑学界痛失“中国塔王”

著名建筑历史学家、建筑教育家路秉杰先生因病医治无效,于2025年5月29日在上海逝世,享年90岁。他生前是中国建筑学会史学分会原副理事长,同济大学建筑与城市规划学院教授及博士生导师。路秉杰出生于1935年,山东聊城人。他在中日两国建筑史、园林史以及中国伊斯兰教建筑等领域…

武汉警界科技精英万松因公牺牲 幕后英雄悄然离去

5月27日0时33分,湖北省武汉市公安局科技信息化支队警务技术四级主任万松因公牺牲,享年50岁。两天后的早晨,武昌殡仪馆告别大厅内庄严肃穆,花圈环绕,哀乐低回。他的遗属、生前亲友及同事百余人含泪忍悲,送别这位警界科技精英。5月26日早上7时30分,青年民警王国栋送完孩子…

UniGUI DBGrid 实现多选

设置unidbgrid属性添加多选框 Options —> dgRowSelect、dgCheckSelect、dgMultiSelect —> True 属性完成后&#xff0c;保存运行&#xff0c;unidbgrid前面就会出现多选框&#xff0c;多选时质检选中前面的多选框或者 shift鼠标左键 将两次点击范围内的数据全选 或是 c…

常见位运算总结

位运算 常见位运算总结位1的个数比特位计数汉明距离只出现一次的数字 常见位运算总结 位1的个数 191. 位1的个数 给定一个正整数 n&#xff0c;编写一个函数&#xff0c;获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数&#xff08;也被称为汉明重量&#xf…

离散化算法的二分法应用

我们思考一个问题&#xff1a;其实这里的二分法回归本源也是基于下标映射的原理&#xff0c;只是实现是借助二分的形式。 在排序好的数组中对目标数值进行二分搜索&#xff0c;在 O(logn) 的时间复杂度内找到该数值是整体数据中的第几个。 具体的我们可以如下操作&#xff1a; …

字节流操作:InputStream类 读取文件的操作(三种 read 方法)

字节流操作&#xff1a;InputStream类 和 OutputStream类 文章目录 字节流操作&#xff1a;InputStream类 和 OutputStream类观前提醒&#xff1a;InputStream类 读取文件的操作&#xff08;三种 read 方法&#xff09;1. 不带参数的 read( )方法&#xff0c;返回值是&#xff…

day13 leetcode-hot100-22(链表1)

160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 1.哈希集合HashSet 思路 &#xff08;1&#xff09;将A链的所有数据存储到HashSet中。 &#xff08;2&#xff09;遍历B链&#xff0c;找到是否在A中存在。 具体代码 /*** Definition for singly-linked list.* pu…

《在人间》葛铮:以无言演绎孤独,肢体语言传递情绪

如何塑造一个全剧中几乎没有台词的角色?葛铮认为关键在于认真体验角色的内心,由心而发地去感受,在镜头前自然地表现。5月28日,他出演的高概念意象情感剧《在人间》播出,他在剧中饰演铁林一角,多数镜头中只能用肢体语言、面部表情等方式传递角色的情绪,这对葛铮来说无疑是…

【docker部署】 Windows版docker部署harbor镜像

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Windows版docker部署harbor镜像 Windows版dock…

TopCode之手撕快排

题目链接 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 使用数组分三块的思想 i用来遍历整个数组 left用来标记<key的边界 right用来标记>key的边界 然后i进行遍历,数组就分成了四块 [l,left]<key [left1,i-1]key [i,right-1]未…

vue2使用node版本启动差异

node 14.21.3启动 无需添加去除ssl的环境变量&#xff0c;npm run dev即可 node 20.12.2 版本需要添加 SET NODE_OPTIONS–openssl-legacy-provider && "dev": "SET NODE_OPTIONS--openssl-legacy-provider && vue-cli-service serve"其…

母亲多种工具打孩子10多次被立案 强制报告制度显效

近日,司法部发布了一起未成年人法律援助典型案例。江苏省某小学老师发现学生胡某某身上有多处新旧伤痕,询问后得知胡某某因不愿意上学等问题被母亲多次殴打。老师随即按照强制报告制度要求向检察机关报告。经鉴定,胡某某挫伤面积达体表面积8%,已构成轻伤一级。随后,其母亲…

多家银行5年期定存利率跌破1.3% “存5年不如存1年”现象频现

离新一轮存款降息仅过去10天,部分中小银行存款利率开始出现剧烈调整。5月30日,多家农商行、村镇银行集体宣布下调定期存款利率。五年期整存整取利率最低降至1.20%,已低于六家国有大行、招商银行等大行1.30%的存款挂牌利率水平。“存5年不如存1年”这样的现象在中小银行中并不…

塞尔维亚一军工厂突发爆炸致7人受伤 工人制作炸药时意外引爆

当地时间30日早上7时40分,塞尔维亚军工企业“克鲁希克”位于瓦列沃市的工厂发生爆炸事故,导致7名工人受伤送医。伤者均为轻伤,已出院居家休养。事故发生在工人制作军用炸药过程中,压机上的引信被意外激活引发爆炸。有1名工人头部受伤,4人出现耳鸣症状。爆炸发生后厂区秩序…

专家解读印度“阵风”折翼背后较量 体系战斗力的较量

印巴冲突中,双方出动了大批战机进行猛烈交火。过去,印巴冲突多为地面战斗,而此次出现了大规模空中作战。这不仅是双方作战平台性能的直接较量,更是两国空战体系的深层碰撞。5月7日,印度武装部队发起代号为“朱砂”的行动,打击巴基斯坦和巴控克什米尔地区的设施。随后,巴…

长沙有花店将艾草花束卖到98元 节日氛围推高需求

端午节除了吃粽子,中国人还有在门口挂艾草的习俗。为了了解长沙市场上艾草的供应情况和价格,记者进行了走访。主打节日氛围的艾草出现在了超市和花店,经过商家精心搭配后成为一束束艾草花束。在河西的一家盒马超市内,摆放着多款艾草花束,价格从19.9元到39.9元不等。盒马相…

买到演唱会铁窗视角票被退款 视野不良引争议

近日,网友小鱼在京观看某明星演唱会时遭遇了视野被遮挡的问题。她表示,自己花费1960元购买了两张票,但到达现场后发现看台边缘的护栏遮住了大半个舞台,导致视线受阻。小鱼立即向工作人员反映情况并要求换座,但被告知没有空位。随后,小鱼录下了座位视角的视频,并找到多名…

王毅会见瑞士联邦委员兼外长卡西斯 共促国际调解院发展

2025年5月30日下午,中共中央政治局委员、外交部长王毅在香港会见了出席国际调解院公约签署仪式的瑞士联邦委员兼外长卡西斯。王毅表示,卡西斯专程来港参加此次活动,体现了瑞士对建立国际调解院的支持以及在多边主义和对话协商化解分歧方面的积极态度。中方赞赏瑞士在国际事务…