消息队列介绍

消息队列

为什么要使用消息队列?有何优缺点?

优点

  • 解耦

    A系统产生的数据,BCDF系统都需要,如果不适用消息对垒,他们就是强耦合,如果使用消息队列的话,只需要将产生的数据发送到MQ中,谁需要直接去MQ中消费就好了,实现了解耦合。

  • 异步

    如果A系统接收一个一个请求,需要本地写库,还需要再BCD系统写库,本地需要3ms,BCD需要的时间长一点,加起来时间就很长,如果使用了MQ,直接发送三次到MQ,花费时间很少,就很快

  • 削峰

    高峰期来的时候,我们消费能力有限,无法全部消费,会有短暂的挤压,但是高峰期一过,就能快速消费掉积压的请求。

缺点

  • 系统可用性降低

    系统引入的外部依赖越多,越容易挂掉。

  • 系统复杂度提高

    要解决的问题更多,比如重复消费,请求丢失等问题

  • 一致性问题

如何保证消息队列的高可用

基于主从做高可用性,以RabbitMQ为例,有三种模式,单机模式,普通集群模式,镜像集群模式

  • 普通集群模式

    无可用性,本地demo练习用

  • 普通集群模式

    多台机器上启动 RabbitMQ实例,但是你创建的queue,只会放在一个RabbitMQ实例上,但是每个实例都会同步queue的元数据(元数据是一些配置信息,通过元数据,可以找到queue所在的实例)。消费的时候如果连接到另一个实例,那么会从queue所在的实例上来取数据。

  • 镜像集群模式

    和普通集群不一样,这种模式下,无论元数据还是queue都同步到所有实例

如何保证不重复消费

在消息中打上标签,隔一段时间提交消费过的消息。还可以在提交到数据库时做一次判断,只要不重复插入到数据库就行。

如何保证消息可靠性传输

  • 生产者弄丢了数据
  1. RabbitMQ开启事务,channel.txRollback

生产者将数据发送到RabbitMQ的时候数据丢失。如果消息没有被RabbitMQ接收到,生产者会收到异常报错,可以使用回滚事务channel.txRollback,然后重发消息,如果收到了消息提交事务。这个有个弊端就是太耗性能,导致吞吐量会下来。一般不用这个模式

  1. 使用confirm机制

    开启之后,每次写的消息都会分配唯一的ID,如果写入到了RabbitMQ中,RabbitMQ会回传一个ack消息,告诉生产者这个消息ok了,如果写入失败,会回调一个nack接口,告诉这个生产者这个消息失败了,重发。

这个还有一个重要的是,这个是事务是同步的,提交事务会阻塞,当时confirm是异步的。

  • RabbitMQ弄丢了消息

解决这个问题需要开启RabbitMQ持久化,两个步骤,第一个创建queue的时候将其设置为持久化,这样RabbitMQ持久化queue的元数据,但是它是不会持久化queue里的数据的,第二就是发送消息的时候将deliveryMode设置为2,将消息持久化,此时RabbitMQ就会将消息持久化到磁盘。但是这样还有隐患,就是还没来得及持久化到磁盘的时候,进程就挂了,所以我们必须要结合confirm机制使用,就是持久化成功之后再发送ack消息。

  • 消费端弄丢了数据

    这个时候还是需要结合confirm机制,就是关闭自动ack,通过API调用就行,每次代码处理完成之后再发送ack。

如何保证消息的顺序性

拆分多个queue,每个queue一个consumer。

消息队列延时了,消息队列满了。

  1. 先修复consumer的问题,让他继续消费。
  2. 让后扩容,建立10倍的queue。
  3. 写一个零时分发数据的consumer程序,消费完直接轮询写入临时建立好的10倍数量的queue。
  4. 用更多的机器部署consumer
  5. 当快速消费完之后,恢复架构