有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家
消息队列的基本作用?
- 异步通信:消息队列提供了异步通信的能力,发送方可以将消息发送到队列中,而无需等待接收方立即处理。发送方和接收方可以解耦,彼此不需要直接交互,从而实现解耦和异步处理。例如im
- 解耦应用程序:消息队列使得不同的应用程序之间可以通过消息进行通信,而不需要直接调用或知道对方的存在。每个应用程序只需关注自己的业务逻辑,将消息发送到队列中,由其他应用程序异步地处理这些消息。这样可以降低应用程序之间的依赖性,提高系统的可维护性和扩展性。
- 缓冲和削峰填谷:消息队列可以作为一个缓冲区,当生产者发送消息的速度快于消费者处理消息的速度时,消息可以暂时存储在队列中。这样可以平衡生产者和消费者之间的速度差异,避免系统的过载和性能问题。
- 数据分发:消息队列可以将消息广播给多个订阅者或消费者。这对于实现发布-订阅模式或者广播通知非常有用,一个消息可以同时被多个订阅者接收并处理。
- 重试和错误处理:消息队列可以处理消息传递过程中的错误情况。当消息发送失败时,消息队列可以自动进行重试,并保证消息的可靠传递。此外,可以将处理失败的消息放入死信队列中进行后续的错误处理和分析。
- 顺序性保证:某些消息队列支持按照特定的顺序发送和处理消息,确保消息的有序性。这对于需要按照特定顺序处理消息的场景非常重要,例如订单处理、事件日志等。
消息队列的基本作用是提供可靠、高效、异步的消息通信机制,实现系统之间的解耦、异步处理、削峰填谷、数据分发和错误处理等功能。它在分布式系统、微服务架构和大规模应用中发挥着重要的作用。
消息队列的优缺点有哪些?
优点:
- 异步通信:消息队列支持异步通信,发送方将消息发送到队列中,无需等待接收方的即时响应,提高了系统的并发处理能力和响应速度。
- 解耦应用程序:通过消息队列,应用程序之间的耦合度降低。每个应用程序只需关注自己的业务逻辑,通过发送和接收消息进行通信,从而提高了系统的可维护性和扩展性。
- 缓冲和削峰填谷:消息队列作为一个缓冲区,可以平衡生产者和消费者之间的速度差异,避免系统的过载和性能问题。它可以处理突发的请求量,实现削峰填谷的效果。
- 数据分发和广播:消息队列支持将消息广播给多个订阅者,实现发布-订阅模式,方便实现数据分发和广播通知。
- 可靠性和持久性:消息队列通常具有可靠的消息传递机制,可以确保消息的可靠性和持久性。即使在消息传递过程中出现故障,消息队列也可以保证消息的传递和处理。
缺点:
- 复杂性:使用消息队列需要引入额外的组件和技术,增加了系统的复杂性和维护成本。需要考虑消息的序列化、消息传递协议、消息队列的配置等问题。
- 一致性问题:由于消息队列的异步特性,消息的发送和处理可能存在一定的时间延迟,因此在一些场景下可能需要额外的机制来处理一致性问题。
- 部署和维护成本:引入消息队列需要部署和维护额外的中间件,涉及到部署、监控、故障排查等工作,增加了系统的运维成本和学习成本。
- 数据一致性和顺序性问题:某些消息队列无法保证消息的严格顺序性,对于一些场景需要保证严格顺序的消息处理可能会有一定挑战。
- 引入单点故障:消息队列本身可能成为系统中的单点故障,如果消息队列出现故障,会影响整个系统的正常运行。
在选择是否使用消息队列时,需要综合考虑系统的需求、复杂性和可靠性等因素,权衡利弊。消息队列在很多场景下是非常有用的,但也需要根据
如何保证消息队列的高可用?
- 集群部署:将消息队列部署在多台服务器上形成集群,通过负载均衡的方式将请求分发到不同的节点。这样可以提高系统的吞吐量和可用性,并且在某个节点故障时仍然能够正常提供服务。
- 数据复制和同步:对于主从架构的消息队列,需要将数据进行复制和同步,以实现数据的冗余备份和容错。在主节点写入消息后,通过数据同步机制将数据复制到备份节点,确保数据的可靠性和持久性。
- 故障自动转移:配置故障自动转移机制,当消息队列节点出现故障时,自动将请求转移到备份节点上,以保证服务的连续性。可以使用类似于主备切换、选举机制或者自动发现机制来实现故障转移。
- 监控和报警:建立监控系统,实时监测消息队列的运行状态、吞吐量、延迟等指标。设置报警机制,及时发现并处理潜在的问题,确保问题得到及时解决,提高可用性。
- 数据备份和恢复:定期对消息队列中的数据进行备份,确保在数据丢失或损坏的情况下能够进行数据恢复。备份数据可以存储在独立的存储介质或者其他节点上,以提供数据的冗余和可靠性。
- 消息队列监控和管理工具:使用专业的消息队列监控和管理工具,可以对消息队列进行实时监控、性能分析、故障排查和性能调优。这些工具提供了丰富的指标和图表,帮助管理员更好地了解系统状态并采取相应措施。
- 容量规划和水平扩展:根据系统的负载和需求进行容量规划,预估消息队列的并发请求量、存储容量等。当负载增加时,通过水平扩展的方式增加节点数量,以提供更好的性能和可用性。
通过以上策略的综合应用,可以提高消息队列的可用性,确保系统能够在故障情况下保持正常运行,并且具备数据的可靠性和持久性。根据具体的需求和系统架构,还可以采用其他高可用技术,如多活部署、数据分片等来进一步提高消息队列的可用性。
如何保证消息消费的幂等性?
其他问法:如何保证消息不被重复消费?
-
关系型数据库主键或唯一键+事务
弊端:
- 性能问题(对整个消息消费逻辑加事务,如果消费逻辑太负责,再有数据库本身的性能并不是很高)
- 不支持事务的部分回滚(除了事务部分还调用了第三方系统,修改了redis)
-
拆解方案
将没一步都做成幂等性的,例如库存系统减完库存后,发一条消息给订单系统对应的消息队列,订单系统单独实现幂等性,订单系统消费成功后给下游系统发消息队列,下游系统单独实现幂等性…
缺点:麻烦
-
通用解决方案(去事务方案)
- 查询有没有消费(可以用关系型数据库,也可以用redis等)
- 记录状态
- 修改状态
- 通知消息队列消费成功了
需要考虑的三个问题
- 消息已经消费成功了,第二条消息将被直接幂等处理掉(消费成功)。
- 并发场景下的消息,依旧能满足不会出现消息重复,即穿透幂等挡板的问题。
- 支持上游业务生产者重发的业务重复的消息幂等问题。
为啥要加延迟消费?
解决并发问题
为啥要加过期时间?
防止第一条消息因为特殊原因消费失败(也没删掉),后续的延迟消费一直重试,最后进入死信队列
参考文章:https://jaskey.github.io/blog/2020/06/08/rocketmq-message-dedup/
https://github.com/Jaskey/RocketMQDedupListener
主要分两方面:一方面是生产者加一个唯一标识,两一方面消费者检查唯一标识,可以借助数据库的主键索引或者唯一键索引,缓存记录消费掉的消息,必要时添加回退机制,防止消费到一半时异常.
无论是何种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。
只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念,每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。
那造成重复消费的原因? 就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。
如何解决?这个问题针对业务场景来答分以下几点
(1)给这个消息做一个唯一主键,做数据库insert,如果出现重复消费情况,会导致主键冲突,避免数据库出现脏数据。
(2)update 和 delete 支持天然幂等性,拿到这个消息做redis的set的操作,那就容易了,不用解决,set操作天然幂等操作。
(3)第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
- 唯一标识符:为每个消息生成一个唯一的标识符,并将其与消息一起存储在消费端。在处理消息之前,先检查是否已经处理过具有相同标识符的消息,如果已经处理过,则直接跳过,避免重复处理。
- 幂等标识字段:在消息中添加一个幂等标识字段,用于标识消息的唯一性。消费端在处理消息时,先检查该字段的值,如果已经处理过具有相同标识字段值的消息,则跳过处理。
- 乐观锁:对于并发环境下的消息消费,可以使用乐观锁来保证幂等性。在消费端处理消息时,先获取并锁定消息的相关资源,然后再检查是否已经处理过。如果已经处理过,则直接释放资源,避免重复处理。
- 数据库约束:在消费端的数据库表中,可以添加唯一约束或者主键约束来保证数据的唯一性。当消费端尝试将消息写入数据库时,如果违反了约束条件,则表明消息已经被处理过,可以忽略该消息。
- 日志记录:在消费端处理消息时,记录消息的处理状态和结果。如果后续收到相同消息,可以先查询日志记录,判断是否已经处理过。
- 幂等性检查和处理:在消费端处理消息时,先进行幂等性检查。可以通过查询数据库、查看缓存状态或者调用特定接口来检查消息是否已经处理过。如果已经处理过,则直接返回成功,否则继续处理消息。
- 事务性操作:在消费端进行消息处理时,将消息处理过程放在一个事务中。通过使用数据库事务或者分布式事务,可以保证消息的处理是原子性的,即使在处理过程中出现异常或者重试,也能保证幂等性。
通过以上方法的应用,可以保证消息的消费是幂等的,即使在重复消费或者异常情况下,也能保证处理的结果是一致的。根据具体的业务场景和系统架构,可以选择合适的方式来实现消息消费的幂等性。
如何保证消息的可靠性传输?
其他问法:如何处理消息丢失的问题?
https://zhuanlan.zhihu.com/p/59759422
如何保证消息的顺序性?
乱序的原因之一:
Consumer从MQ里面读取数据是有序的,但是每个Consumer的执行时间是不固定的,无法保证先读到消息的Consumer一定先完成操作,这样就会出现消息并没有按照顺序执行,造成数据顺序错误。
rabbitmq
-
全局有序
将所有消息发送到同一个队列里,队列只有一个消费者,预取值设置为1
缺点:性能问题
-
局部有序
多建几个队列
讲需要保持顺序的一批数据发送到一个队列里面(例如根据订单号hash求模)
大量消息在 MQ 里长时间积压,该如何解决?
找原因:是生产者突增还是消费者故障?
如果消费者故障先修消费者
增加队列,增加消费者,快速消费
MQ 中的消息过期失效了怎么办?
查找原因:为什么过期了?过期时间设置的太短了?消费者端出问题了,导致消息挤压了?
如果消费者端出问题了先修复问题,然后查询过期的数据,找流量不高的时候导入
只能重新查询数据,手动灌入消息队列
RabbitMQ 有哪些重要的角色?
-
生产者
-
消费者
-
代理:就是RabbitMQ本身,用于扮演快递的角色,本身并不生产消息
RabbitMQ 有哪些重要的组件?
- ConnectionFactory(连接管理器):应用程序与RabbitMQ之间建立连接的管理器
- Channel(信道):消息推送使用的通道
- Exchange(交换器):用于接受、分配消息
- Queue(队列):用于存储生产者的消息
- RoutingKey(路由键)
RabbitMQ 有几种广播类型?
- fanout(扇出):所有 bind 到此 exchange 的 queue 都可以接收消息;
- direct(直连):通过 routingKey 和 exchange 中的 bindingKey 决定的那个唯一的 queue 可以接收消息;
- topic(主体):所有符合 routingKey 所 bind 的 queue 可以接收消息。
Kafka 可以脱离 zookeeper 单独使用吗?为什么?
Kafka 不能脱离 zookeeper 单独使用,因为 Kafka 使用 zookeeper 管理和协调 Kafka 的节点服务器。
补充:最新版本的 Kafka 2.8.0 版本实现了 Raft 分布式一致性机制,意味着可以脱离 ZooKeeper 独立运行
Kafka 有几种数据保留的策略?
1、按照过期时间保留(到时见就删除)
2、按照存储的消息大小保留(占用指定大小的磁盘后删除老消息)