消息队列原理

【消息队列的定义】

消息队列是在消息的传输过程中保存消息的容器;

用于接收消息,并以文件的形式存储;

一个消息队列可以被一个或者多个消费者消费;

三个元素:

Producer:消息生产者,负责生产和发送消息到Broker;

Broker:消息处理中心,负责消息存储、确认、重试等;包含多个Queue;

Consumer:消息消费者,负责从Broker中获取消息,并进行相应处理;

【消息队列的抽象过程】消息队列原理_第1张图片

过程描述:

消息队列原理_第2张图片

1、消息生产者发送消息至MQ,等待MQ返回ACK;

2、MQ收到消息,将消息保存在MQ中;(先保存在内存,为了确保高可靠性,进行消息存盘,持久化到磁盘文件)

3、MQ消息存盘成功后,响应ACK给生产者;

4、MQ将消息推送给对应的消费者,等待消费者返回ACK;

5、如果消费者在指定时间内成功返回了ACK,则MQ判定消息消费成功,删除磁盘文件中的消息,执行步骤6;如果MQ在指定时间内没有收到ACK,则MQ判定消息消费失败,会尝试重试推送消息,重复执行4/5/6三步;

6、MQ删除磁盘文件中的消息;

文件系统:

1、落盘地点:部署MQ服务器的虚拟机/物理机的文件系统;

2、刷盘方式:异步刷盘和同步刷盘;

【刷盘原理】

以RocketMQ为例,详细分解异步刷盘和同步刷盘的原理:

消息队列原理_第3张图片

适用场景:

同步刷盘:数据可靠性高,适用于金融等对数据可靠性要求高的场景,性能比较低;

异步刷盘:性能和吞吐量高,Broker端异常关闭时,会有少量消息丢失;

同步刷盘和异步刷盘的区别:

同步刷盘是消息写入内存的page Cache后,立即通知刷盘线程刷盘,然后等待刷盘完成。刷盘线程完成后唤醒等待的线程,返回消息写成功的状态;

异步刷盘是消息写入内存的page Cache后,立即返回写成功的状态,故而吞吐量大;当内存的page Cache积累到一定程度后,统一出发写磁盘动作,快速刷盘写入;

一般有两种,有两种方式进行读写

  1. 第一种,Mmap+PageCache的方式,读写消息都走的是pageCache,这样子读写都在pagecache里面不可避免会有锁的问题,在并发的读写操作情况下,会出现缺页中断降低,内存加锁,污染页的回写。
  2. 第二种,DirectByteBuffer(堆外内存)+PageCache的两层架构方式,这样子可以实现读写消息分离,写入消息时候写到的是DirectByteBuffer——堆外内存中,读消息走的是PageCache(对于,DirectByteBuffer是两步刷盘,一步是刷到PageCache,还有一步是刷到磁盘文件中),带来的好处就是,避免了内存操作的很多容易堵的地方,降低了时延。比如说缺页中断降低,内存加锁,污染页的回写。

【思考题】(说明:下面以RabbitMQ为例)

1、生产者发送消息时,MQ如何防止丢失?

解决方案:

1.1 生产者发送消息确保成功:开启ACK机制;

(1)消息投递出去后,等待MQ响应一个ACK确认;

(2)如果没有ACK确认,在记录错误日志,然后开启重试;

1.2 MQ收到消息确保不丢失:开启持久化机制;

(1)消息队列的交换机要持久化;

(2)消息队列的队列要持久化;

(3)消息队列的消息要持久化;

(4)设置刷盘的规则,改用同步刷盘;

2、消费者消费消息时,MQ如何确保消费?

2.1 消费者消费消息确保成功:开启ACK机制;

(1)消息消费后,响应一个ACK确认给MQ,MQ收到ACK确认,则删除该消息;

(2)如果没有ACK确认,则将这个消息进入下一轮投递给其他消费者;或者加入死信/延迟队列;

2.2 通过业务逻辑确保消费成功:开启回调机制;

(1)消费者成功消费消息后,通过通知的方式告知生产者,则生产者不重推消息;

(2)如果在10分钟或者设定的时间内,没有通知,则生产者重推消息;

3、生产者推送消息时,如何解决重复推送消息?

(1)生产者业务逻辑中,严格根据ACK通知来记录好每条消息推送的结果,以此可以减少重复推送消息;

(2)消息体重自定义一个雪花算法的ID,以此标识消息的唯一性,在推送MQ前先将此消息的唯一标识入库或者暂存redis,然后推送消息时以此为标志判断是否推送成功;

4、消费者消费消息时,如何解决重复消费消息?

(1)根据生产者推送消息中的唯一性标识,消费者在消费的时候做好去重;(数据库表或者redis)

(2)消费者在消费消息时,考虑分布式部署的特点,需要根据消息唯一性标识添加分布式锁;

5、MQ如何保证消息顺序消费?

5.1为什么要顺序消费?

场景如:在用户下单之后,会发送创建订单、减扣库存的消息,但要保证减扣库存在创建订单之后执行;那就要求顺序消费了;

5.2 为什么会不按顺序消费?

由于消费者是集群部署,导致多个消费者能够消费同一个queue的消息;如果创建订单和减扣库存的消息都发送到同一个queue中,则不同的消费者就会有不同的顺序;

5.3 如何解决,确保消息顺序消费?

将Queue拆分,创建多个Queue,同类型的消息定义一个队列,然后创建多个消费者,每个消费者对应一个Queue;然后每个消费者在业务中使用分布式锁控制执行的顺序;

【消息队列模式】

点对点模式:多个生产者可以向同一个消息队列发送消息,一个具体的消息只能由一个消费者消费

消息队列原理_第4张图片

发布/订阅模式:单个消息可以被多个订阅者并发的获取和处理

消息队列原理_第5张图片

【消息队列应用场景】

1、应用解耦 :不同服务通过MQ进行通信,而不用关心彼此的实现细节;

2、异步处理:消息队列的生产者、消费者本来就异步;

3、削峰填谷:当上下游系统处理能力存在差距的时候,我们要严格要求自己

4、日志处理:(当大量的日志需要处理的时候,kafka是首选)

5、消息通讯:内置了高效的通信机制,因此可以在纯消息通讯、点对点消息队列,聊天室等功能谨慎使用;

6、消息广播:如果没有消息队列,每当一个新业务,都要接入一次新接口。

有兴趣的,可以私信我,进一步沟通交流!

你可能感兴趣的:(java,分布式,开发语言)