RabbitMQ之消息模式简单易懂,超详细分享~~~
前言
上一篇对RabbitMQ的流程和相关的理论进行初步的概述,如果小伙伴之前对消息队列不是很了解,那么在看理论时会有些困惑,这里以消息模式为切入点,结合理论细节和代码实践的方式一起来学习。
正文
常用的模式有Simple、Work、Fanout、Direct、Topic、Headers,可以通过设置交换机类型和配置参数来实现各个模式;接下来就分别进行实操演示吧。
以下演示都是通过管理员的账号进行。其实每种模式其实很大一部分操作都是一样的,所以公共部分不会重复截图说明,不过会针对不同的配置进行说明。
1. 简单模式(Simple)
简单模式顾名思义就是简单,不用配置太多的东西,如下图所示:
上图解析:
P:表示生产者,负责推送消息;
C:表示消费者,负责接收消息;
中间红色部分:代表的是队列(Queue);
小伙伴可能会奇怪,这里没有交换机吗?
其实是有的,上一篇说流程的时候,消息肯定是要通过交换机转发到队列中的,这里没有指定,那是因为用到了默认的交换机,具体看以下演示。
1.1 Web管理界面进行演示
对于Web界面演示来说,只需要将消息能生产、投递、消费即可,我们不用去弄一个生产者和消费者,生产者和消费者都是业务处理逻辑用的,所以通常都是根据业务需求就行实现的;话不多说开始演示吧。
根据上图所示,我们只需要创建一个队列即可,然后就可以进行消息模拟发送和消费了。
此时并没有指定交换机绑定,点击队列名看详情中的Bindings,有一个默认的交换机已经和队列进行绑定:
队列详情页面的说明,在上篇文章中就已经标注了,这里就不再赘述。
有了绑定关系之后,就可以在默认的交换机页面开始模拟转发消息;首先进入Exchanges管理页面,点击默认交换机(AMQP default)进入详情开始发布消息:
消息发送成功之后就会在队列界面看到消息情况:
队列里面有了消息之后,就可以模拟消费者进行消息消费,点击队列名进入详情,可在详情也模拟消费:
如上所示,简单模式整个消费流程就通过Web页面模拟完了。但在消费消息时,提供了Ack Mode模式(消息确认模式)选择来进行消费,可选择的模式如下:
- Nack message requeue true:获取消息,但是不会向Server做ack应答确认(即不告诉服务器消息被消费了),消息重新入队。即队列中的消息不会被删除掉;
- Automatic Ack:获取消息,向Server做应答确认(即会告诉服务器消息被消费了),消息不重新入队,将会从队列中删除;
- Reject requeue true:拒绝获取消息(即拒绝处理消息),消息重新入队;
- Reject requeue false:拒绝获取消息(即拒绝处理消息),消息不重新入队,将会被删除;
到这关于简单模式下的界面演示就结束了,其中描述的细节内容是共用的,在其他模式下的操作也类似,后续不做重复说明。
1.2 代码进行演示
这里就用控制台的方式,一步一步的实现。这里需要引入Nuget包:RabbitMQ.Client。生产者的整体代码如下:
接下来就一步一步来调试,看看消息是怎么一步一步发出去的;
创建连接
刚开始没有任何连接,如下:
代码继续下一步,连接就有了:
此时就可以理解为网络连接上了,但通道还没有创建出来,如下:
根据连接创建通道
通道根据连接进行创建,目的是为了提高传输效率,共用一个连接,不然频繁的创建和销毁连接会占资源,影响性能。
定义队列
有连接和通道之后理论就可以直接发消息了,但直接通信会相互依赖比较强,达不到解耦合的效果。所以需要定义一个队列将消息存放到里面,客户端想用了自己来消费就行,另外队列还可以达到一定的削峰作用。
创建队列的时候需要传几个参数,分别意思如下:
参数1:queue, 队列的名称;
参数2:durable, 队列是否持久化;如果为true,服务器重启之后队列还在,不会被清除;否则就被清掉。
参数3:exclusive ,是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭;
参数4:autoDelete, 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息;
参数5:arguments, 用来设置队列附加参数,如设置队列的有效期、队列的消息生命周期、消息的最大长度等;
发送消息
经过以上步骤,就可以发送消息了,如上没有定义交换机,那就是绑定了默认交换机。
发送时的几个参数意思如下:
参数1:exchange,交换机,这里没有指定交换机。
参数2:routingKey,路由key,即指定队列,简单模式下,路由key默认就是队列名
参数3:basicProperties, 配置其他相关属性
参数4:body ,需要发送的消息内容
以上的生产者完成了,现在再来一个消费者演示一下消息消费,消费者整体代码如下:
效果如下:
在整个过程中,还是会先建立连接,创建通道,指定队列; 不同的是增加对接收数据的处理。
是不是用起来比较简单方便,主要是在复杂项目中,消息队列的作用真的很大。来,接着往下说说其他模式。
2. 工作模式(Work)
工作模式是考虑到多个消费者情况下,消息如何被消费的,主要有两种方案,轮询分发和公平分发;
- 轮询分发:消费者依次轮着消费消息,直到消息消费完为止,按均分配。
- 公平分发:根据消费者能力进行分发,即处理快的消费就多,处理慢的就消费就少,能者多劳。
上图解析:
- P:表示生产者,负责推送消息;
- C1、C2:表示多个消费者,都可以从同一个队列接收消息;
- 中间红色部分:代表的是队列(Queue);
这两种方式需要多个消费者,在界面不太好模拟,所以就直接上代码演示了。
2.1 轮询分发
在简单模式基础上稍微改动一下代码即可。在生产者中发多条消息出来,然后启用多个消费者就可以进行模拟如下:
生产者代码整体如下:
消费者代码整体如下:
两个消费者的代码都是一样,没有变动; 两个消费者的都是在消费同一个队列的消息,可以先启动两个消费者,然后在启动生产者,效果如下:
由上可见,默认情况下其实采用的是轮询方式。
2.2 公平分发
在实际业务中,有些业务处理比较耗时,有些处理耗时不长,如上案例,假如奇数消息需要处理比较耗时,那么对应的消费者就压力比较大。这种情况可以通过公平分发的方式进行业务处理,处理快点的就多处理点。
如何才能知道消费者处理业务完成呢?消费者处理完成之后主动上报是最好不过的,所以只需要在消费者端将自动确认机制改为手动确认即可,即:业务处理完成之后,手动上报确认状态。
生产者的代码不需要变动,只需要稍微改改消费者代码即可,这里模拟两个不同处理能力的消费者:
- 消费者1处理一条消息业务需要500毫秒;
- 消费者2处理一条消息业务需要2秒;
消费者1整体代码如下:
消费者2整体代码如下,主要是模拟时间不一样:
先启动两个消费者,再启动生产者,看看消费情况,如下:
以上演示就是公平分发模式的演示,其中有两个关键的步骤:
- 设置每次消费的消息条数,可以根据实际业务情况配置,这里设置的是每次取一条。通过 channel.BasicQos进行设置。
- 将消息确认模式改为自动模式,这样就可以根据实际业务处理情况反馈确认信息,服务器就会将消息处理掉,即删除消息。所以一般在实际业务场景大都会推荐使用手动确认的方式,这样避免业务未处理导致消息就被服务器给清掉的情况。
3. 发布订阅模式(Fanout)
Fanout模式是一种发布订阅模式,是一种广播机制,不需要指定路由Key。这种模式的交换机就会将消息广播到绑定的所有队列上去,只要有消费者订阅对应的队列,就会收到消息。如下图:
上图解析:
- P:表示生产者,负责推送消息;
- X:表示交换机,图中表示一个交换机绑定了多个队列;
- C1、C2:表示多个消费者,都可以从同一个队列接收消息;
- 中间红色部分:代表的是队列(Queue);
在这种模式下,消息会一次性被多个消费者消费。
3.1 Web管理界面进行演示
先创建一个Fanout模式的交换机;
创建两个队列(根据实际需要创建多个),并将队列绑定到上一步创建的交换机上;
这里演示就创建两个队列,分别是FanoutQ1和FanoutQ2
在队列详情页或交换机详情页都可以进行交换机和队列的绑定,这里分别在FanoutQ1和FanoutQ2队列详情页进行绑定,如下:
同样的方式绑定FanoutQ2, 绑定完成之后,可以在交换机详情页看到对应的绑定队列:
通过交换机上投递消息,看效果;
此时通过FanoutExchange交换机投递消息,绑定到此交换机上的队列都能收到:
查看队列消息情况,可以看到两个队列都接收到消息了。
3.2 代码进行演示
其实代码和操作Web一样,只是用代码实现生产者和消费者而已。
生产者的代码
因为Fanout交换机不用关注RoutingKey,所以在发布消息时,第二个参数不需要传递RoutingKey。
消费者1的代码
消费者比较关注的是交换机需要和生产者指定的是同一个,队列和交换机有绑定。
消费者2的代码,其实和消费者1基本一样,只是定义的队列不一样
先启动消费者,然后启动生产者,看效果
这里是控制台程序,为了显示方便就先启动消费者,后期的生产者,实际应用场景先启动谁都行。
4. 路由模式(Direct)
Direct模式是在Fanout基础增加RoutingKey条件, 即交换机不会将消息现全部投递到所有队列,而是只投递到对应RoutingKey下的队列。如图:
上图解析:
- P:表示生产者,负责推送消息;
- X:表示交换机,指定类型为direct,图中表示一个交换机绑定了多个队列;
- 中间箭头上的error、info、warning代表具体的RoutingKey;
- C1、C2:表示多个消费者,都可以从同一个队列接收消息;
- 中间红色部分:代表的是队列(Queue);
Direct模式其实在实际应用场景中用的比较多的,默认的Exhanges也是Direct模式, 很多关于消息队列的框架,默认也是采用这种模式,主要原因是根据RoutingKey精确的处理对应的业务,不会由于考虑不周到,导致消息处理有不确定性,性能相对也不错。
4.1 Web管理界面进行演示
先创建一个Direct模式的交换机;
创建两个队列,然后将队列绑定到上一步创建的交换机上,并指定对应的RoutingKey;
创建队列完成之后,还需要绑定到交换机上,上一种模式演示的是从队列详情中维护绑定关系,这次从交换机详情中进行演示,如下:
绑定第1个队列,指定RoutingKey是order,模拟处理订单的:
绑定第2个队列,指定RoutingKey是msg,模拟处理消息的:
有了绑定关系,就可以测试验证了。
注:这里的RoutingKey可以根据实际情况随意指定的。
向交换机上投递消息,看效果;
模拟发布一个RoutingKey为order的消息:
再发布一个消息,指定RoutingKey为msg,如下:
两个消息都发布成功,而是指定对应的RoutingKey进行投递,所以现在绑定到此交换机上两个队列中分别有一条数据,查看队列的消息概况:
可以进入队列详情,通过GetMessage获取到具体的消息内容,这里就不截图了,上面已经演示过。
4.2 代码进行演示
在Fanout的代码基础上稍微改动即可,主要改动点就是改变交换机的类型,并在队列和交换机绑定时设置对应的RoutingKey,发布消息的时候指定交换机和RoutingKey。
生产者代码
消费者1代码,主要是绑定队列时指定的RoutingKey为order
消费者2代码,主要是绑定队列时指定的RoutingKey为msg
运行起来看效果:
如上演示效果,和Web演示一样,只有精确匹配到RoutingKey才能消费到对应的消息数据。
5. 主题模式(Topic)
Topic模式是在Direct模式基础增加模糊匹配RoutingKey,Direct精确匹配RoutingKey,Topic可以通*或#进行模糊匹配,从而把消息投递到对应的队列中,如图:
上图解析:
- P:表示生产者,负责推送消息;
- X:表示交换机,指定类型为topic,图中表示一个交换机绑定了多个队列;
- 中间箭头上的文字代表模糊匹配的RoutingKey;其中*表示匹配RoutingKey中的一个词,#号表示匹配RoutingKey的零个或多个词,匹配符需要与点号(.)搭配使用。 *.orange.test.# 示例中orange算一个词,test算一个词,即通过点号(.)分开的就称为一个词。
- C1、C2:表示多个消费者,都可以从同一个队列接收消息;
- 中间红色部分:代表的是队列(Queue);
5.1 Web管理界面进行演示
先创建一个Topic模式的交换机;
创建两个队列,然后将队列绑定到上一步创建的交换机上,并指定对应的RoutingKey;
将队列绑定到交换机上,这里还是在交换机详情中进行演示,如下:
同样的步骤分别对TopicQ1和TopicQ2进行规则绑定,如下:
现在的TopicExchange的绑定关系如下:
有了关系之后就可以进行验证效果了。
向交换机上投递消息,会根据RoutingKey的模糊匹配规则将消息投递到对应的队列中,看效果;
先指定order.create.test发布消息,看看会匹配哪些队列:
TopicQ2接收到消息,匹配到路由规则order.#
再指定RoutingKey 为order.update 发布一个消息,如下:
TopicQ1和TopicQ2都收到消息了,匹配到路由规则order.#和order.*,如下:
再指定RoutingKey 为order发布一个消息,就会匹配到order.#,这里就不截图了。
以上测试说明:在Topic类型交换机和队列绑定关系时,可以指定RoutingKey的匹配规则,星号、#号、点号搭配使用,其中*表示匹配RoutingKey中的一个词,#号表示匹配RoutingKey的零个或多个词。
更多情况,小伙伴们自己动手试试。
5.2 代码进行演示
在Direct的代码基础上稍微改动即可,主要改动点就是改变交换机的类型,并在队列和交换机绑定时设置对应的RoutingKey,这里的RoutingKey是一个规则,是星号、#号、点号和每个词的组合,发布消息的时候指定交换机和RoutingKey,RoutingKey会去匹配绑定的规则。
生产者代码
消费者1代码,指定路由匹配规则为order.#
消费者2代码,指定路由匹配规则为order.*
演示效果,将生产者和消费者都启动
如上图,和Web演示一样,#号匹配0个和多个词,*号只能匹配一个词。 符号可以与词任意组合,小伙伴可以根据业务情况自行发挥。
6. 参数模式(Headers)
Headers模式不是通过RoutingKey进行匹配投递消息,而是匹配请求头中所带的键值进行消息投递,所以创建队列是需要设置绑定的头部信息,有两种模式:全部匹配和部分匹配。
- 全部匹配:x-match=all,表示所有的键值都匹配了才行。
- 部分匹配:x-match=any,表示只要其中有键值对匹配就行。
5.1 Web管理界面进行演示
先创建一个Headers模式的交换机;
创建两个队列,然后将队列绑定到上一步创建的交换机上,可以指定Headers的参数;
将队列绑定到交换机上,这里还是在交换机详情中进行演示,如下:
这里不使用RoutingKey的方式,而是通过设置参数的形式进行绑定,后续投递消息的时候就匹配参数,如果能匹配上,就将消息投递到对应的队列。
绑定HeaderQ1队列:
同样的方式绑定HeaderQ2队列,只是只添加了一个键值对,order:111,最后HeaderExchange交换机的绑定关系如下:
关系绑定好之后就可以进行测试效果了。
向交换机上投递消息,会根据检查Headers参数的条件是否符合,若符合将消息投递到对应的队列中,看效果;
设置两个参数进行发布,如下:
可以看到两个队列都匹配到了,因为order和msg键值对匹配到HeaderQ1,order的键值对匹配到HeaderQ2,如果只设置一个order简直对呢:
此时只有HeaderQ2才能精确匹配,HeaderQ1没有全部匹配,所以对应队列没有收到消息,如下:
由此可见,在界面上没有指定x-match绑定的话,默认是all,就是要全部匹配才投递消息到对应队列。
这里继续新增一个HeaderQ3的队列,创建方式和上面不一样,只是在绑定交换机的时候增加x-match 为 any,如下:
绑定成功之后,现在关系如下,其中order的键值对是在每个绑定中都有,如下:
测试发消息之前,把之前消息都清空了,也就是队列中的消息都是空的,这次我们再指定order为111的参数进行发布消息,看看有哪些队列收到消息呢:
消息发出后,之后HeaderQ2和HeaderQ3收到消息,HeaderQ2只有一个order参数,精确匹配上了,HeaderQ3有多个参数,但设置了x-match为any,所以只要匹配其中一个即可。 HeaderQ1多个参数需要全部匹配才行,所以没有接收到消息:
5.2 代码进行演示
Headers模式是根据参数进行匹配,不是通过RoutingKey,所以只需要在绑定队列时设置好参数,在发送消息的时候也设置好参数,这样就会根据匹配原则去匹配参数,如果匹配上,消息就投递到对应的队列,供消费者进行消费。这里为了演示比较清晰一点,使用一个生产者,三个消费者的形式进行演示。
生产者
代码中关键的部分是指定交换机的类型为Headers,然后模拟了两类参数的形式投递消息,方便用于测试参数匹配模式的测试。
消费者1,绑定参数为order:111和msg:222
消费者2,和消费者1代表基本一样,只是绑定参数不一样;
消费者3,代表基本和消费者1一样,只是在参数指定的时候增加了x-match来指定匹配模式,这里指定为any,也就是只要其中有部分匹配上也可以消费到消息。
演示效果,启动生产者和消费者:
如上图,消费者3指定了匹配模式为部分匹配,所以可以接收到一个参数的消息,而消费者1需要精确匹配,所以不能接收到。
关于常用的消息模式就聊到这吧,小伙伴们可以根据自己的业务场景进行使用,相关演示代码的地址如下:
码云:https://gitee.com/CodeZoe/dot-net-core-study-demo/tree/main/RabbitMQDemo
总结
RabbitMQ提供了多种模式应对各种业务,但匹配条件越是模糊或者参数化,那性能相对比较弱。再回顾一些常用的消息队列组件,是不是很多都是默认使用Direct模式,精确匹配的RoutingKey可以针对具体不同业务处理,匹配性能也相对比较高; 当然其他模式小伙伴也可以针对业务情况进行使用。
后续的文章将继续分享RabbitMQ消息确认机制、死信队列、磁盘监控等相关知识点的应用,关注“Code综艺圈”,和我一起学习吧。
RabbitMQ之消息模式简单易懂,超详细分享~~~的更多相关文章
- RabbitMQ六种队列模式-简单队列模式
前言 RabbitMQ六种队列模式-简单队列 [本文]RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...
- RabbitMQ之消息模式(下)
目的: RabbitMQ之消息模式(上):https://www.cnblogs.com/huangting/p/11994539.html 消费端限流 消息的ACK与重回队列 TTL消息 死信队列 ...
- RabbitMQ之消息模式2
消费端限流 什么是消费端的限流? 假设一个场景,首先,我们RabbitMQ服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现下面情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法 ...
- RabbitMQ之消息模式
目的: 消息如何保证100%的投递 幂等性概念 Confirm确认消息 Return返回消息 自定义消费者 前言: 想必知道消息中间件RabbitMQ的小伙伴,对于引入中间件的好处可以起到抗高并发,削 ...
- RabbitMQ之消息模式1
消息100%的投递 消息如何保障100%的投递成功? 什么是生产端的可靠性投递? 保障消息的成功发出 保障MQ节点的成功接收 发送端收到MQ节点(Broker)确认应答 完善的消息进行补偿机制 BAT ...
- Spring 事务配置管理,简单易懂,详细 [声明式]
Spring 事务配置说明 Spring 如果没有特殊说明,一般指是跟数据存储有关的数据操作事务操作:对于数据持久操作的事务配置,一般有三个对象,数据源,事务管理器,以及事务代理机制: Spring ...
- RabbitMQ六种队列模式-工作队列模式
前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列 [本文]RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...
- RabbitMQ六种队列模式-发布订阅模式
前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅 [本文]RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...
- RabbitMQ六种队列模式-路由模式
前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式 [本文]RabbitMQ六种队列模式-主 ...
随机推荐
- Linux编辑shell脚本快速启动jar包
1.上传jar包到服务器 2.创建并编辑start.sh文件 vi start.sh 将下面内容复制到文件中 ps -ef|grep xf-demo |grep -v grep |awk '{prin ...
- linux中CentOS配置文件编辑错误撤回
未编辑状态下 U键 撤销 DD 快速删除
- Map集合概述和Map常用子类
概述java.util.Map接口 Map<K,V> 有两个泛型 类型参数:K - 此映射所维护的键的类型V - 映射值的类型 特点:1.Map集合是双列集合,一个元素包含两个值,一个是k ...
- vue2升级vue3指南(一)—— 环境准备和构建篇
1.nodejs和npm 注意二者的版本,版本过低需要升级,本人升级后的版本如下: $ node -v v16.15.1 $ npm -v 8.11.0 2.package.json 和依赖升级 由于 ...
- Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo
前言 上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍.基础使用,并将其封装一层Qt. 本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt ...
- Java之struts2框架学习
Java之struts2框架学习 About Struts2 Struts也是一款MVC框架 , Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全 ...
- 【线性DP】数字三角形
题目链接 原题链接 题目描述 给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大. 7 3 ...
- docker部署练习
三个部署任务 docker部署nginx docker pull nginx #拉取nginx镜像 docker images #检查拉取的镜像 docker run -d -p 3344:80 -- ...
- OpenCV视频防抖技术解析
视频防抖有很多种技术,各有优劣,主流的目前分为三种:EIS电子防抖EIS电子防抖是通过软件算法实现防抖的.其技术运作原理是通过加速度传感器和陀螺仪模块侦测手机抖动的幅度,从而来动态调节整ISO.快门以 ...
- 一步一步在angular11中添加多语言支持
1.新建angular 2.添加@angular/localize ng add @angular/localize 3.设置默认locale_id,在app.module.ts中 import { ...