摘要: 使用RabbitMQ的消息队列,可以有效提高系统的峰值处理能力。
RabbitMQ是消息代理(Message Broker),它支持多种异步消息处理方式,最常见的有:
Work Queue:将消息缓存到一个队列,默认情况下,多个worker按照Round Robin的方式处理队列中的消息。每个消息只会分配给单个worker。
Publish/Subscribe:每个订阅消息的消费者都会收到消息,因此每个消息通常会分配给多个worker,每个worker对消息进行不同的处理。
RabbitMQ还支持Routing、Topics、以及Remote procedure calls (RPC)等方式。
对于不同的消息处理方式,有一点是相同的,RabbitMQ是介于消息的生产者和消费者的中间节点,负责缓存和分发消息。RabbitMQ接收来自生产者的消息,缓存到内存中,按照不同的方式分发给消费者。RabbitMQ还可以将消息写入磁盘,保证持久化,这样即使RabbitMQ意外崩溃了,消息数据不至于完全丢失。
为什么使用RabbitMQ?
最简单的一点在于,它支持Work Queue等不同的消息处理方式,可以用于不同的业务场景。RabbitMQ的Work Queue,即消息队列。
使用消息队列,可以将不算紧急、但是非常消耗资源的计算任务,以消息的方式插入到RabbitMQ的队列中,然后使用多个处理模块处理这些消息。
这样做最大的好处在于:提高了系统峰值处理能力。因为,来不及处理的消息缓存在RabbitMQ中,避免了同时进行大量计算导致系统因超负荷运行而崩溃。而那些来不及处理的消息,会在峰值过去之后慢慢处理掉。
另一个好处在于解耦。消息的生产者只需要将消息发送给RabbitMQ,这些消息什么时候处理完,不会影响生产者的响应性能。
安装并运行RabbitMQ
使用Docker运行RabbitMQ非常简单,只需要执行一条简单的命令:
sudo docker run -d --name rabbitmq -h rabbitmq -p 5672:5672 -v /var/lib/rabbitmq:/var/lib/rabbitmq registry.docker-cn.com/library/rabbitmq:3.7
-d : 后台运行容器
–name rabbitmq : 将容器的名字设为rabbitmq
-h rabbitmq : 将容器的主机名设为rabbitmq,希望RabbitMQ消息数据持久化保存到本地磁盘是需要设置主机名,因为RabbitMQ保存数据的目录为主机名
-p 5672:5672 : 将容器的5672端口映射为本地主机的5672端口,这样可以通过本地的5672端口访问rabbitmq
-v /var/lib/rabbitmq:/var/lib/rabbitmq:将容器的/var/lib/rabbitmq目录映射为本地主机的/var/lib/rabbitmq目录,这样可以将RabbitMQ消息数据持久化保存到本地磁盘,即使RabbitMQ容器被删除,数据依然还在。
消费者的消息确认机制
rabbitmq怎么知道消息被接受了呢?
acknowledge 确认机制 用手动模式
work消息模型(任务模型)
队列里的消息越来越多,内存总有存满的一天消息会丢失,消息堆积。耗时较长的比如发短信,需要用mq。
让多个消费者绑定一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。
channel.basicQos(1); 设置每次消费一个信息,解决消息堆积问题。
2.3.订阅模型分类
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机到达队列,实现一个消息被多个消费者获取的目的
X(Exchanges):交换机一方面:接收生产者发送的消息。另一方面:知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
Exchange类型有以下几种:
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key 的队列
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
我们这里先学习
Fanout:即广播模式
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
订阅模型-Fanout
1) 可以有多个消费者
2) 每个消费者有自己的queue(队列)
3) 每个队列都要绑定到Exchange(交换机)
4) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
5) 交换机把消息发送给绑定过的所有队列
6) 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
两个变化:
1) 声明Exchange,不再声明Queue
2) 发送消息到Exchange,不再发送到Queue
步骤:
// 获取到连接
// 获取通道
// 声明队列
// 绑定队列到交换机
// 定义队列的消费者
// 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
// 监听队列,手动返回完成
订阅模型-Direct
有选择性的接收消息
在订阅模式中,生产者发布消息,所有消费者都可以获取所有消息。
在路由模式中,我们将添加一个功能 - 我们将只能订阅一部分消息。 例如,我们只能将重要的错误消息引导到日志文件(以节省磁盘空间),同时仍然能够在控制台上打印所有日志消息。
但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
在Direct模型下,队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
消息的发送方在向Exchange发送消息时,也必须指定消息的routing key
绑定队列到交换机,同时指定需要订阅的routing key。需要 update、delete
订阅模型-Topic
Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!
Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
通配符规则:
#:匹配一个或多个词
*:匹配不多不少恰好1个词
举例:
audit.#:能够匹配audit.irs.corporate 或者 audit.irs
audit.*:只能匹配audit.irs
面试题:
如何避免消息丢失?
1) 消费者的ACK机制。可以防止消费者丢失消息。
2) 但是,如果在消费者消费之前,MQ就宕机了,消息就没了。
是可以将消息进行持久化呢?
要将消息持久化,前提是:队列、Exchange都持久化
durable 持久化
AmqpTemplate 统一的spring消息处理模板;