问题有哪些?
(1)消息队列为什么会出现?
(2)消息队列能用来干什么?
(3)使用消息队列存在的问题?
(4)如何解决重复消费的问题?
(5)如何解决消息的顺序消费问题?
(6)如何解决分布式事务的问题?
(7)如何解决消息堆积的问题?
1、什么是消息队列?
消息队列是一个存放消息的容器,消息队列是分布式系统中重要的组件之一,使用消息队列主要是为了通过异步处理提高系统性能和流量削峰,降低系统的耦合性
2、 为什么使用消息队列?
① 通过异步处理提高系统性能(减少响应所需时间)。
② 削峰/限流。
③ 降低系统耦合性。
3、使用消息队列存在的问题
① 系统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了!(需要考虑消息是否丢失或挂掉)
② 系统复杂性提高: 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!(需要保证消息不被重复消费、丢失、传递的顺序前后一致等问题)
③ 一致性问题: 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!(数据保存到MQ中,消息可能未被消费者正确消费,导致数据不一致)
4、JMS和AMQP
JMS(Java Message Service,java消息服务)是java的消息服务,JMS的客户端之间可以通过JMS服务进行异步的消息传输。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
ActiveMQ就是基于JMS规范实现的。
JMS两种消息模型: ①点对点(P2P)模型 ②发布/订阅(Pub/Sub)模型
AMQP,一个提供统一消息服务的应用层标准 高级消息队列协议(二进制应用层协议),是应用层协议的一个开放标准,为面向消息的中间件设计,兼容JMS。RabbitMQ就是基于AMQP协议实现的。
5、常见的消息队列对比
总结:
①ActiveMQ的 性能比较差 ,版本迭代很慢,不推荐使用。
②RabbitMQ在吞吐量方面虽然稍逊于Kafka和RocketMQ,但是由于它基于 erlang开发,所以 并发能力很强,性能极其好,延时很低,达到微秒级 。如果业务场景对并发量要求不是太高(十万级、百万级),那这四种消息队列中,RabbitMQ一定是你的首选。如果是大数据领域的 实时计算、日志采集等场景,用Kafka 。
③RocketMQ阿里出品,Java系开源项目,但接口这块不是按照标准JMS规范走的,有些系统要迁移需要修改大量代码。
④Kafka仅仅提供较少的核心功能,却可以实现 超高的吞吐量,ms级的延迟 ,极高的可用性以及可靠性,而且 分布式 可以任意扩展。同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量。kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性,天然适合大数据实时计算以及日志收集。
RabbitMQ内容:
1、什么是死信队列?如何导致的?
DLX,全称为 Dead-Letter-Exchange,死信交换器,死信邮箱。当消息在一个队列中变成死信 (dead message) 之后,它能被重新发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列 。
导致死信的几种原因:
①消息被拒(Basic.Reject /Basic.Nack) 且 requeue = false。
②消息TTL过期。
③队列满了,无法再添加。
2、什么是延迟队列?RabbitMQ怎么实现延迟队列?
延迟队列 指的是存储对应的延迟消息,消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。(发送到队列后,消费者不能立马消费,得等待一定时间才能消费,该队列即为延迟队列)
RabbitMQ 自身没有延迟队列的,那么如何实现延迟队列?
①通过RabbitMQ本身队列的特性来实现,需要使用RabbitMQ的 死信交换机(Exchange)和消息的存活时间TTL(Time To Live) 。
②在RabbitMQ3.5.7及以上的版本提供了一个 插件(rabbitmq-delayed-message-exchange)来实现延迟队列功能 。同时,插件依赖Erlang/OPT 18.0及以上。
也就是说,AMQP协议以及RabbitMQ本身没有直接支持延迟队列的功能,但是可以通过TTL和DLX模拟出延迟队列的功能。
3、什么是优先级队列?
RabbitMQ自V3.5.0有优先级队列实现, 优先级高的队列会先被消费 。
可以通过x-max-priority参数来实现优先级队列。不过,当消费速度大于生产速度且Broker没有堆积的情况下,优先级显得没有意义。
4、如何保证RabbitMq的顺序性?
4.1、错乱场景
错乱场景1 :一个queue
,有多个consumer
去消费,这样就会造成顺序的错误,consumer
从MQ
里面读取数据是有序的,但是每个consumer
的执行时间是不固定的,无法保证先读到消息的consumer
一定先完成操作,这样就会出现消息并没有按照顺序执行,造成数据顺序错误。
错乱场景2 :一个queue
对应一个consumer
,但是consumer
里面进行了多线程消费,这样也会造成消息消费顺序错误。
4.2、解决方案
解决方案一
拆分成多个queue
,每个queue
一个consumer
,就是多一些queue
而已,确实是麻烦点;这样也会造成吞吐量下降,可以在消费者内部采用多线程的方式取消费。
解决方案二
或者就一个queue
但是对应一个consumer
,然后这个consumer
内部用内存队列做排队,然后分发给底层不同的worker
来处理
5、如何保证消息的可靠性?
(1)消息有效期:默认情况下,如果在发送消息时,不设置消息过期时间,那么消息时永不过期的,即使消息没有被消费掉,消息而会一直存储在队列中。
(2)TTL:消息存活时间,即消息的有效期,给消息设置 TTL
让消息存活一定的时间,如果消息存活时间超过了 TTL
并且还没有被消息,那么消息就会变成死信,添加到死信队列。
在RabbitMQ中,有两种方式给消息设置TTL:
- 方式一:在声明队列时,可以给队列设置消息的有效期,这样所有进入该队列的消息都会有一个相同的有效期
- 方式二:在发送消息时,设置消息的有效期,这样不同的消息就具有不同的有效期
- 如果两个都设置了,以时间短的为准
当给消息设置有效期后,如果消息过期没有被消费就会被从队列中删除,进入到死信队列,但是这两种方式对应的删除时机有点差异:
- 方式一:当为消息队列设置过期时间时,消息过期了就会被删除,因为消息进入 RabbitMQ后是存在一个消息队列中,队列的头部是最早要过期的消息,所以 RabbitMQ只需要一个定时任务,从头部开始扫描是否有过期消息,有的话就直接删除
- 方式二:当消息过期后并不会立马被删除,而是当消息要投递给消费者的时候才会被删除,因为第二种方式,每条消息的过期时间都不一样,想要知道哪条消息过期,必须要遍历队列中的所有消息才能实现,当消息比较多时这样就比较耗费性能,因此对于第二种方式,当消息要投递给消费者的时候才去删除
(3)进入死信队列的几类情况
- 消息被拒绝(
Basic.Reject/Basic.Nack
) ,并且设置requeue
参数为false
- 消息过期
- 队列达到最大长度
(4)发布确认+消息回退 的机制 https://blog.csdn.net/XZB119211/article/details/127008216
6、如何保证RabbitMq的高可用性?
RabbitMQ是比较有代表性的,因为是 基于主从(非分布式) 做高可用性的,我们就以RabbitMQ为例子讲解第一种MQ的高可用性怎么实现。RabbitMQ 有 三种模式:单机模式、普通集群模式、镜像集群模式 。
- 单机模式
- 普通集群模式:多台机器上启动多个RabbitMQ实例,每个机器启动一个。
- 镜像集群模式:即RabbitMQ的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的queue,无论元数据还是queue里的 消息都会存在于多个实例上 ,就是说,每个RabbitMQ节点都有这个queue的一个完整镜像,包含 queue 的全部数据的意思。