Neo's Blog

不抽象就无法深入思考
不还原就看不到本来面目!

0%

消息队列-概览

可能的一些作用:

  1. 非核心逻辑异步化,追求高性能
  2. 解除耦合,Event Driven 事件驱动设计
  3. 实现广播
  4. 削峰填谷,把峰值流量缓冲下来,后面慢慢处理

具体可以用于:

  1. 分布式事务,单方生产,多个消费业务逻辑
  2. 数据复制:通过消息队列,将数据复制到多个目的地(多维度数据表、ES、Hadoop、搜索等)
  3. 日志同步:多个app生产日志并放入队列,然后消费队列完成日志的离线与实时处理
  4. 延迟队列:可靠的延迟队列,分布式环境定时器
  5. 广播通知:Cache失效通知

消息队列的缺点

系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整?MQ挂了,整套系统崩溃了,你不就完了么。

系统复杂性提高:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已

一致性问题:A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。

主流消息队列对比

特性 ActiveMQ RabbitMQ RocketMQ kafka
单机吞吐量 万级,吞吐量比RocketMQ和Kafka要低了1个数量级 同ActiveMQ 10万级,可支撑高吞吐 10w量级
topic数量对吞吐量的影响 topic可达几百几千,吞吐小幅下降,一大优势 topic从几十到上百,吞吐大幅下降;单机支持topic不宜过多,如有需要可以加更多机器
MasterSlave 主-从 主-从 物理Master-Slave 逻辑上Master-Slave,按照Partition
分布式消息事务 支持 支持 不支持
延迟消息 支持 不支持
消息投递实时性 RocketMQ使用长轮询,同Push方式实时性一致,消息的投递延时通常在几个毫秒 Kafka使用短轮询方式,实时性取决于轮询间隔时间
消费失败重试 RocketMQ消费失败支持定时重试,每次重试间隔时间顺延 Kafka消费失败不支持重试
消息顺序 RocketMQ支持严格的消息顺序,在顺序消息场景下,一台Broker宕机后,发送消息会失败,但是不会乱序 支持 Kafka支持消息顺序,但是一台Broker宕机后,就会产生消息乱序
主从选举 不需要选举NameServer KRaft选举
时效性 ms us,最大特点,延迟低 ms ms
可用性 高,基于主从架构实现高可用性 同ActiveMQ 非常高,分布式架构 非常高,分布式,单数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 较低概率丢数据 经过参数优化配置,可以做到0丢失 同RocketMQ
功能支持 极其完备 基于erlang开发,所以并发能力很强,性能极其好,延时很低 MQ功能较为元善,还是分布式的,扩展性好 功能较为简单
总体优劣势 早期使用,社区不活跃 吞吐低,语言不熟,开源社区活跃,小公司 阿里品牌保障(利弊)要求技术研发力量,大公司 大数据领域

Master/Slave概念差异
Kafka:Master/Slave 是个逻辑概念,1 台机器同时是 Master 和 Slave。
RocketMQ:Master/Slave 是个物理概念,1 台机器只能是 Master 或者 Slave。

参考:https://blog.csdn.net/wdj_yyds/article/details/123534023

分布式消息队列评价指标

可靠

分布式消息队列提供更好的可靠性,主要体现在:

  1. 消息会被持久化到分布式存储中。这样避免了单台机器存储的消息由于机器问题导致消息的丢失;
  2. 不佳的网络环境中,保证只有当消息的接收者确实收到消息时才从队列中删除消息。

可扩展

可扩展性体现在访问量和数据量两个方面:

访问量:分布式消息队列服务,会随着访问量的增减而自动增减逻辑处理服务器;

数据量:当数据量扩大时,后端分布式存储会自动扩容。

安全

安全体现在以下两个方面:

  1. 同时使用消息队列的业务之间不会互相干扰。如果有多个业务同时在使用消息队列,对于单机的消息队列服务,一个业务的消息操作可能会影响其他业务的正常运行。比如,一个业务的消息操作特别频繁,占据了消息队列的绝大部分服务时间,也占据了这台服务器的绝大部分网络IO,导致其他业务无法正常地与消息队列通信。而且甚至可能由于服务控制不当,导致机器崩溃,服务停止,业务也跟着停止。分布式消息队列则不会出现这个问题:
    (1)监控措施完善,系统性能指数会控制在一定范围之内,而且有任何异常也会报警;
    (2)当访问量和数据量增大时,分布式消息队列服务可以自动扩展。

  2. 各业务的消息内容是安全存储的,其他业务不能访问到非自身业务的数据。
    一方面是业务需要密钥来访问消息队列;另一方面,消息是被加密存储的。

简单实用

简单实用体现在:

  1. 透明:接收者和发送者无需知道具体的消息队列的服务器地址,服务器的增减对接收者和发送者透明。
  2. 实用:对于两个服务之间不能通信的网络情况,消息队列为他们提供了恰到好处的桥梁。

如何保证消息不丢

生产端不丢消息

生产端如何保证不丢消息呢?确保生产的消息能到达存储端。

如果是RocketMQ消息中间件,Producer生产者提供了三种发送消息的方式,分别是:

  • 同步发送
  • 异步发送
  • 单向发送

生产者要想发消息时保证消息不丢失,可以:

  • 采用同步方式发送,send消息方法返回成功状态,就表示消息正常到达了存储端Broker。
  • 如果send消息异常或者返回非成功状态,可以重试。
  • 可以使用事务消息,RocketMQ的事务消息机制就是为了保证零丢失来设计的

存储端不丢消息

如何保证存储端的消息不丢失呢? 确保消息持久化到磁盘。大家很容易想到就是刷盘机制。

刷盘机制分同步刷盘和异步刷盘:
生产者消息发过来时,只有持久化到磁盘,RocketMQ的存储端Broker才返回一个成功的ACK响应,这就是同步刷盘。它保证消息不丢失,但是影响了性能。
异步刷盘的话,只要消息写入PageCache缓存,就返回一个成功的ACK响应。这样提高了MQ的性能,但是如果这时候机器断电了,就会丢失消息。

Broker一般是集群部署的,有master主节点和slave从节点。消息到Broker存储端,只有主节点和从节点都写入成功,才反馈成功的ack给生产者。这就是同步复制,它保证了消息不丢失,但是降低了系统的吞吐量。与之对应的就是异步复制,只要消息写入主节点成功,就返回成功的ack,它速度快,但是会有性能问题。

消费阶段不丢消息

如何保证消息顺序

消费者执行完业务逻辑,再反馈会Broker说消费成功,这样才可以保证消费阶段不丢消息。

消息队列保证顺序性整体思路就是这样啦。比如Kafka的全局有序消息,就是这种思想的体现: 就是生产者发消息时,1个Topic只能对应1个Partition,一个 Consumer,内部单线程消费。

但是这样吞吐量太低,一般保证消息局部有序即可。在发消息的时候指定Partition Key,Kafka对其进行Hash计算,根据计算结果决定放入哪个Partition。这样Partition Key相同的消息会放在同一个Partition。然后多消费者单线程消费指定的Partition。

如何实现消息事务

下面是RabbitMQ/RocketMQ消息事务的实现机制

image

  1. 生产者产生消息,发送一条半事务消息到MQ服务器
  2. MQ收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
  3. MQ服务器返回ACK确认到生产者,此时MQ不会触发消息推送事件
  4. 生产者执行本地事务
  5. 如果本地事务执行成功,即commit执行结果到MQ服务器;如果执行失败,发送rollback。
  6. 如果是正常的commit,MQ服务器更新消息状态为可发送;如果是rollback,即删除消息。
  7. 如果消息状态更新为可发送,则MQ服务器会push消息给消费者。消费者消费完就回ACK。
  8. 如果MQ服务器长时间没有收到生产者的commit或者rollback,它会反查生产者,然后根据查询到的结果执行最终状态。
你的支持是我坚持的最大动力!