消息队列知识点梳理

消息队列(Message Queue)简称MQ,用于应用程序之间的消息传递,在消息中间件领域有着广泛的应用。高级消息队列协议(Advenced Message Queuing Protocol)简称(AMQP),是应用层协议的一个开放标准。AMQP协议可以使用任何编程语言实现,基于AMQP协议实现的中间件有:RabbitMQ、ActiveMQ、RocketMQ等。

基础部分

使用消息队列的优点

使用消息队列之前,可以先看看消息队列有什么优点,能够解决什么样的问题,也就是为什么要使用消息队列的原因

解耦

使用消息队列前

系统A直接调用系统B、系统C,调用代码是硬编码的,系统之间存在依赖耦合的关系。
如果后续有新的系统需要接入到A系统中,那么就需要通过不断的修改A系统的代码来实现,不利于系统扩展。

使用消息队列后

系统A不直接调用其他系统,而是将消息发送给消息队列,系统A不必关心有多少个系统会消费该消息。后续有新系统需要接入时,只需要订阅消息队列即可。

异步

使用消息队列前

系统A调用一个核心业务很快就完成了,但是后续还需要等待非核心业务完成后才通知用户执行成功,这样等待时间太久了用户会不耐烦。

使用消息队列后

系统A在1ms内完成核心业务的调用并通知用户执行成功,整个过程用户只等待了1ms,很大程度的提高了用户体验。
核心业务成功发送消息到MQ就算业务执行完成了,不必关心后面的非核心业务是否已经消费消息。
在高并发的场景下也经常会使用到这种异步方式,可以对流量进行消峰,将非核心业务放到消息队列中滞后处理,等待资源空闲时从队列中取出消息逐个处理。

使用消息队列的缺点

使用消息队列的优点同样也产生了相应的缺点,要足够了解所可能带来到风险并且根据实际业务场景合理规避风险。

解耦影响系统可用性

引入了消息队列之后,由于各个系统之间采用消息队列进行通信,如果消息队列停止运行了,那么相当于整个架构体系内的系统都变成了不可用。

异步增加系统复杂性

由于网络故障等原因,消费者在消费完成后没有发送确认消息给消息队列,消息队列认为消息没有送达而重复发送消息,导致订阅者收到多次相同到消息重复消费,这时就需要消费者自己处理重复消息。
由于网络故障或者其他不可抗拒的外部因素,如何保证可靠性传输也是要解决的一个问题,主要体现在以下4个方面:
防止重复消费的问题、防止生产者丢数据、防止消息队列丢数据、防止消费者丢数据。

针对消息队列的缺点如何进行完善

如何保证高可用

一般在生产环境中都会采用消息队列集群和主从模式来保证高可用,文章后半部分都以RabbitMQ的实现方式为例。

如何防止重复消费的问题

解决这个问题之前先考虑一下,为什么会收到重复消息?
这里以RabbitMQ为例,正常情况下,消费者在消费完消息之后都会给消息队列发送一个确认信息,消息队列收到该确认信息后会将该消息删除,因为网络故障等原因,消息队列没有收到消费者的确认信息,导致消息队列继续存在该消息,并且下次会重复被消费。
解决的方式可以根据实际业务场景来决定:

  1. 业务系统接收到消息之后一般会有入库操作,将消息的id作为记录主键,一旦收到重复消息就会抛出主键插入异常。
  2. 业务系统将消息处理结果存储到redis中,同样使用消息的id作为key,即使收到同样的消息set了多次也只会有一条消息,因为消息id一样,所以无论设置多少次都会保证幂等性。
  3. 采用redis的key-value方式做一个消费记录,消费者收到消息后先拿消息id去消费记录里查找,如果查找到了说明已经被消费过了就不处理相关业务,如果消息记录里没有该消息的id,则将该消息set到redis记录中,并进行相关业务处理。

    如何保证传输的可靠性

    要保证传输可靠性,就要考虑到整个消息队列前后有可能发生到问题并进行处理。

    防止生产者丢消息

    生产者将消息发送到消息队列到过程中,由于网络故障等因素导致消息队列无法接受到消息。
    RabbitMQ提供了两种解决方案:
  • transaction
    transaction模式采用事务的方式保证生产者消息成功送达到消息队列。发消息前先开启事务,然后再发送消息,发送的过程中如果异常就执行回滚,成功就提交事务。
  • confirm
    采用transaction模式的缺点是每次发送消息前都要先开启事务,这种操作理论上会降低消息队列的吞吐量。
    confirm模式相比transaction模式有着更高的吞吐量,一旦生产者将消息投递到所有匹配到消息队列之后,RabbitMQ就会发送一个ACK给生产者,使生产者知道消息已经正确到达目的队列了。如果没有送达消息队列,消息队列会发送一个Nack消息给生产者,生产者接收到该消息后可以决定是否进行重发。

    防止消息队列丢消息

    开启持久化磁盘,并配合Confirm,消息持久化到磁盘后再给生产者发一个ACK信号,如果持久化磁盘之前MQ就停止运行了,生产者收不到ACK信号会自动进行重发。MQ如果一直停止运行,生产者根据业务场景进行多次重试仍然发送失败,生产者应该实现报警并记录日志。

    防止消费者丢消息

    消费者收到消息队列的消息后会自动发送一个ACK确认消息给消息队列。在接收到消息并发送确认收到信息后,由于消费者自身的业务异常导致消息没有被合理的处理,导致消息丢失。可以根据实际的业务场景,将自动发送确认消息改为手动发送,在消费者业务代码正常执行完成之后再手动确认消息。
0%