RabbitMQ 其实一共有六种工作模式:简单模式(Simple)、工作队列模式(Work Queue)、发布订阅模式(Publish/Subscribe)、路由模式(Routing)、通配符模式(Topic)、远程调用模式(RPC)。其中发布订阅模式、路由模式、通配符模式这三种模型都属于订阅模式,只不过它们之间进行路由的方式不同罢了。远程调用模式是 RPC 不属于MQ,所以最终统计下来就是五种工作模式。
1.简单模式(Simple)
RabbitMQ 的简单模式提供了一种基本的消息传递机制,适合初学者和简单应用。通过了解生产者、消费者、队列和交换机的基本概念,您可以快速上手并实现基本的消息传递功能,其具体流程如下:
注意:生产者到队列之间是有交换机的,一般采用的默认交换机,此处省略了。
结合示意图案例分析: RabbitMQ是一个消息代理,它接受和转发消息。我们可以把它抽象成一个货运仓库,当商家把商品打包放进仓库后,可以确定快递员最后一定会把快递送到收件人手里。
示意图解释: P(Producer / Publisher):生产者, 一个发送消息的用户应用程序。 C(Consumer): 消费者,一个用来等待接收消息的用户应用程序。 Queue(红色区域):消息队列,作用是接收消息、缓存消息,队列只受主机的内存和磁盘限制。生产者将消息发送到队列,队列是存储消息的缓冲区,消费者从队列中获取消息。 应用场景: 简单的发送与接收,没有特别的处理。
简单模式特点: 1、一个生产者对应一个消费者,通过队列进行消息传递。 2、使用默认的direct交换机。
2.工作队列模式(Work Queue)
与简单模式相比,工作队列模式(Work Queue)多了一些消费者,该模式也使用direct 交换机,应用于处理消息较多的情况。 注意:下面的三种订阅模式中,再后半部分的消费中,我们都可以引用工作模式中的多消费者机制,具体如下图:
特点 : 一个队列对应多个消费者,通过队列进行消息传递 一条消息同一时刻只会被一个消费者消费 消息队列默认采用轮询的方式将消息平均发送给消费者 使用Rabbitmq默认交换机direct。
应用场景: 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
存在问题:(1) 工作队列模式 使用了MessageProperties.PERSISTENT_TEXT_PLAIN 来设置消息持久化,目的是为了保证数据安全可靠不丢失。但是,事与愿违。消息虽然被标记为持久化却并不能完全保证消息不会丢失。尽管MessageProperties.PERSISTENT_TEXT_PLAIN 告诉 RabbitMQ 将消息保存到磁盘,但是这里依然存在当消息刚准备存储在磁盘的时候;可能存在还没有存储完的情况,消息还在缓存的一个间隔点。此时并没有真正写入磁盘。因此持久性保证并不强,进而需要引入发布确认、ACK来解决该问题。
(2) 多个消费者进行消息消费,因为消息是轮询平均发送给消费者。可能会有某个消费者Slow;因为要处理其他复杂的业务逻辑,其消费的效率相对其他消费者比较慢,这个就会造成当其他消费者已经消费完处于空闲状态时,因平均分配原则,队列任会继续把消息发给 Slow 处于忙碌状态,大大降低了系统的性能。正确的做法的是"能劳者多劳;消费越快的,让其消费的越多"。
解决方案:(1)设置预取计数:在消费者中设置预取计数,以控制未确认消息的数量 (2)启用手动确认:关闭自动确认,手动确认每条处理完的消息。
原生态中通过在代码中使用int prefetchCount = 1; channel.basicQos(prefetchCount); 进行解决。
Springboot整合版通过在配置文件中使用spring.rabbitmq.listener.simple.acknowledge-mode: manual spring.rabbitmq.listener.simple.prefetch: 1 进行解决。
3.发布订阅模式(Publish/Subscribe)
生产者将消息发送给交换机,交换机将消息转发到绑定此交换机的每个队列中;工作队列模式的交换机只能将消息发送给一个队列 (路由键匹配的队列),发布订阅模式的交换机能将消息发送给多个队列。发布订阅模式使用fanout 交换机。
如果此时每个队列中的任务比较多,此时我们可以定义多个消费者绑定同一个队列,此时就形成了前半部分是订阅模式,后半部分是工作模式,而后半部分的工作模式再消费的时候我们即可以使用默认的轮询平均分配也可以使用能者多劳的分配方式。 如下图:
4.路由模式(Routing)
路由(Routing)模式是发布订阅模式的升级版。我们知道发布订阅模式是无条件地将所有消息分发给所有消费者队列,每个队列中都有相同的消息;路由模式,由下图很容易理解,每个队列消息会因为绑定的路由不同而不同。
特点:1、每个队列绑定一个路由关键字RoutingKey,生产者将带有RoutingKey的消息发送给交换机,交换机再根据路由 RoutingKey关键字将消息定向发送到指定的队列中;2、默认使用 direct 交换机。
**应用场景:**1、如在电商网站的促销活动中,双十一搞促销活动会把促销消息发布到所有队列中去;而一些小的促销活动为了节约成本,只发布到站内信队列。2、为了节省磁盘空间,需要将重要的错误消息引导到日志文件,同时仍然能够在控制台上打印输出所有日志消息。
路由模式是一种精准的匹配,只有设置了 Routing Key 后消息才能进行分发。
5.通配符模式(Topic)
通配符模式(Topic)是在路由模式的基础上升级,给队列绑定带通配符的路由关键字,只要消息的RoutingKey 能实现通配符匹配而不再是固定的字符串,就会将消息转发到该队列。通配符模式比路由模式更灵活。 如下图:
特点: 1、消息设置RoutingKey时,RoutingKey由多个单词构成,中间以 . 分割。2、队列设置RoutingKey时,#可以匹配任意多个单词(包含0个),*可以匹配任意一个单词。3、使用 topic 交换机。
应用场景:通配符模式的匹配规则相对于路由模式要显得抽象,比如工厂生产手机屏,虽然手机屏的品牌(vivo、华为、荣耀)有很多,但是只要有手机屏产出就会发送一条消息给厂商,时刻统计手机屏的数量。路由模式方案:每个手机品牌独占一个队列和交换机绑定一个唯一标识 RoutingKey 手机品牌;通配符模式:一个队列和交换机绑定一个通配的标识 RoutingKey 即可。
注意:(1)在rabbitmq中一个队列可以绑定多个路由键channel.queueBind("SEND_STATION2", ROUTE_NAME, "A"); channel.queueBind("SEND_STATION2", ROUTE_NAME, "B"); 这样SEND_STATION2队列就绑定了两个路由键,只要满足其中一个即会往该队列中添加消息。
(2)多个队列可以绑定相同的路由键channel.queueBind("SEND_STATION1", ROUTE_NAME, "A"); channel.queueBind("SEND_STATION2", ROUTE_NAME, "A"); 这样生产者在推送路由键A的消息时两个队列均可接受到该消息。
(3)一个消费者可以同时绑定多个队列进行消费channel1.basicConsume("A", true, consumer1);channel1.basicConsume("B", true, consumer2); 这样该消费者即可监听A或者B队列
如果你想从底层源码了解Spring Boot整合RabbitMq的原理,如何生产消息推送到交换机,如何进行消费,可以参考以下文章:从源码层级深入探索 Spring AMQP 如何在 Spring Boot 中实现 RabbitMQ 集成------生产者如何将消息发送到 RabbitMQ Exchange-CSDN博客
从源码层级深入探索 Spring AMQP 如何在 Spring Boot 中实现 RabbitMQ 集成------消费者如何进行消费-CSDN博客