MQ全称:Message Queue,是在消息传输过程中保存消息的容器,在分布式系统通信中有比较多的应用场景。
对于一个系统,合理使用MQ,可以使应用解耦,提高系统的容错率。异步提速,提高系统吞吐量和提升用户体验。同时可以做到削峰填谷,提高系统稳定性。当然,系统中引入了一个外部的中间件,会使系统依赖增多,提高了系统复杂度,一旦MQ宕机,会对业务造成影响,所以保持MQ的高可用非常重要。
一、RabbitMQ中的相关概念:
- Broker:接收和分发消息的应用,就是RabbitMQ Server
- Virtual host:基本组件划分到一个虚拟分组中,多个用户可以创建自己的virtual host,在其中创建exchange、queue等
- Connection:生产者和消费者与Broker的TCP连接
- Channel :是在 connection 内部建立的逻辑连接,减少建立TCP连接的开销
- Exchange:message到达Broker后经过交换机分发到队列中,只会转发消息,不存储消息。常见有Fanout:广播,Direct:定向,Topic:通配符三种类型。
- Queue:消息存储的地方,等待消费
- Binding:exchange和queue之间绑定的虚拟连接,用于message的分发依据
二、RabbitMQ的工作模式:
- Hello World,这是一种简单的工作模式,生产者向队列中放消息,消费者消费。
- Work queues,相比于上面最简单的模式,允许多个消费端,多个消费者消费一个队列中的消息,一个消息只会被消费一次,消费者之间是竞争关系。这种模式对于一些任务量大分发处理的场景很适用。
- Publish/Subscribe,发布订阅模式下,多了交换机的角色。生产者将消息交给交换机处理,交换机转发到规则匹配下的队列中,消费者等待消息接收和消费。在这种模式下,交换机与多个队列绑定,一个消息最终可以被多个消费者收到,只不过是从不同的队列消费。很适合数据服务商和应用商这种场景。
- Routing,和发布订阅模式相比,交换机的不能随意绑定了,需要指定RoutingKey,交换机不会将消息转发给所有绑定的队列,只有消息的RountingKey和队列的RountingKey匹配时,队列才能接收消息。
- Topics,通配符模式,和Routing模式相比,匹配RoutingKey的方式使用了通配符。通配符规则:# 匹配一个或多个词,* 匹配1个词,例如:order.# 能够匹配 item.aaa.bbb 或者 order.aaa,order.* 只能匹配 item.aaa。使用通配符模式可以实现发布订阅和Routing模式的功能,且配置更加灵活。
三、生产者消息确认机制和可靠性投递
使用MQ,消息发送方希望能杜绝消息丢失和投递失败的情况,RabbitMQ提供了两种方式,来控制消息的可靠性投递。Confirm:确认模式,Return:回退模式。要明确的是,这是相对于生产者而言的,描述的是消息是否正确的投递到队列中。
消息流转的路径:producer-broker-exchange-queue-consumer
消息到达exchange之后,不管成功或者失败,都有ConfirmCallBack的回调,我们可以知道是否投递成功,从exchannge到queue如果投递失败,会有ReturnCallBack的回调,通过这两个回调就可以直到一条消息是否成功投递,如果失败了,可以进一步处理。
四、消费者消息签收机制:Consumer ACK
消费者是否成功消费消息非常重要,RabbitMQ消费者有三种消息签收方式,none:不处理,被消费即从队列中移出,manual:手动确认,auto:自动确认。为了保证消息消费成功,通常为了保证安全性会使用manual这种确认方式。
五、消费者限流-qos
消费者默认一次从MQ中读取一条消息去消费,可以为消费者配置一次读取多条消息。在实际场景,有时并发量大有时并发量低,但是无法约束生产端,所以我们要对消费端限流,保持后端服务的稳定。注意使用消费端限流,消费者确认消息一定要使用手动确认的方式。
六、TTL特性
TTL全称是Time To live,过期时间,当消息到达存活时间后如果还没有被消费,会被自动清除。可以对消息设置过期时间,也可以对整个队列设置过期时间。针对对列设置表示整个队列的消息都有过期时间。如果两者都设置了,以时间较短的为准。
七、死信队列
消息失效后通过一个交换机转到另一个队列,这个交换机就叫做死信交换机,这种存储消息的方式就是死信队列。
工作中常有延迟处理的场景,在rabbitmq中是基于TTL特性和死信队列处理的。当我们一条消息设置了存活时间投递到一个队列(Q1)之后,如果在存活时间内没有被消费,这个消息会通过死信交换机转发到另一个队列(Q2),延时处理实际上消费的就是Q2。一般来说消息在以下场景我们会让消息存到死信队列:设置存活时间未被消费、原队列长度达到限制、消费者拒收消息,本意是这些消息失效后不直接丢失,还有再次处理的机会。
还有额外的一点,使用消息时我们一定要通过代码和数据库各种手段来保证业务的幂等性,这个对数据安全很重要。