消息队列进阶知识扫盲

技术选型

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量万级,比RocketMQ 、Kafka 低一个数量级同 ActiveMQ10万级,支持高吞吐10万级,高吞吐,一般结合大数据类的系统进行实时数据计算日志采集等场景
topic 数量对吞吐量的影响topic 可以达到几百/几千的级别,吞吐量会有比较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topictopic从几十到几百个时候,吞吐量会大幅度下降,在同等机器下, Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic ,需要增加更多的机器资源
时效性ms 级微秒级,这是RabbitMQ的一大特点,延迟最低ms级延迟在ms级以内
可用性高,基于主从架构实现高可用同 ActiveMQ非常高,分布式架构非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性有较低的概率丢失数据基本不丢经过参数优化配置,可以做到0丢失同 RocketMQ
功能支持MQ 领域的功能极其完备基于 erlang 开发,并发能力很强,性能极好,延迟很低MQ 功能较为完善,还是分布式的,扩展性好功能比较简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用
  • 技术实力一般,技术挑战不是特别高,选择 RabbitMQ
  • 基础架构研发实力较强,选择 RocketMQ
  • 大数据领域的实时计算、日志采集,选择 Kafka 是业内标准

为什么使用消息队列

业务场景

解耦

image-20230723143518460image-20230723143531124

一个系统或模块调用了多个系统或模块,互相之间的调用很复杂,维护起来很麻烦,且该调用时不需要同步调用接口的,用 MQ 给它异步化解耦。

异步

image-20230723143737435

image-20230723143749781

削峰

image-20230723143819413

image-20230723143827528

缺点

系统可用性降低

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

系统复杂度提高

  • 如何保证消息没有重复消费?

  • 怎么处理消息丢失的情况?

  • 怎么保证消息传递的顺序性?

一致性问题

高可用性

RabbitMQ 的高可用性

基于主从(非分布式)

单机模式

普通集群模式(无高可用性)

特点
  • 多个 RabbitMQ 实例
  • 创建的 queue 只会放在一个 RabbitMQ 实例上,每个实例都会同步 queue 的元数据(queue的一些配置信息,通过元数据,可以找到 queue 所在实例)
  • 根据queue 元数据从 queue 所在的实例拉取数据

image-20230723150657326

缺点
  • 没做到所谓的分布式,要么有数据拉去的开销,要么单实例性能瓶颈
  • 存放 queue 的实例宕机了,导致无法拉取;如果开启了消息持久化,消息不一定会丢,得要等该实例恢复才能拉取数据

镜像集群模式(高可用性)

特点
  • queue 的元数据和消息都会存在于多个实例上,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据

  • queue 更新,会自动同步到多个实例的 queue 上

  • 如果节点宕机,还有其他节点有 queue 的完整数据

image-20230723152534157

缺点
  • 同步花销大,导致网络带宽压力和消耗很重
  • 不是分布式的,没有扩展性,某个 queue 负载很重,新增机器没用,没有办法线性扩展 queue

Kafka 的高可用性

  • 由多个 broker 组成,每个 broker 是一个节点
  • 一个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就存放一部分数据
  • 天然的分布式消息队列,一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据

Kafka 0.8 以前,没用HA机制,partition 没有备份(副本),broker宕机,该 topic 的 partition 的数据就丢失了

image-20230723153352514

Kafka 0.8 以后,提供HA机制,replica(复制品)副本机制

  • 每个 partition 的数据会同步到其他机器上,形成自己的多个 replica 副本
  • 所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,其他 replica 就是 follower
  • 写数据,leader 负责同步数据到所有 follower 上
  • 读数据,直接读 leader
  • 均匀地将一个 partition 的所有 replica 发布到不同的机器上,提高容错性

image-20230723153801088

  • 如果某个 broker 宕机,partition 在其他 broker 存在副本
  • 若宕机的 broker 由 某个 partition 的 leader,则 follower 会重新选举一个新的 leader 出来
  • 写数据:生产者写 leader,leader 写到 disk,其他 follower 主动从 leader pull 数据,同步完成会发送 ack 给 leader,leader 收到所有 follower 的 ack 后会响应成功给生产者
  • 消费:从 leader 读,只有一个消费被所有 follower 同步成功返回 ack 时,该消息才能被消费者读到

幂等性

保证消息不被重复消费

Kafka

  • 每个消息写入都有一个 offset ,代表消息的序号

  • consumer 消费消息后,定时定期把自己消费过的消息的 offset 提交,表示消费过了

  • 若是重启也继续上次消费到的 offset 继续消费

  • 可能 consumer 消费消息后,没有提交 offset,重启之后,少数消息会再次消息一次image-20230723171038735

  • 考虑重复消费之后,应该如何保证幂等性

    • 入库,根据主键查询,已经存在则更新
    • 写 Redis,天然幂等性
    • 生产者发送数据附加全局唯一的 id,消费前根据 id 去检查是否消费过(如使用 Redis)

可靠性

  • 如何处理消息丢失的问题?

RabbitMQ

image-20230723171533147

Producer 丢失

事务机制

  • 开启事务,如果消息没有成功被 RabbitMQ 接收到,则 回滚事务 Producer 会重试发送消息,成功接收再提交事务
  • 同步阻塞,吞吐量下降,过度消耗性能

confirm 机制

  • 每次写消息分配唯一 id,写入成功返回 ack,失败则回调 nack 接口,通知 Producer 接收失败
  • Producer 可以定期超时重试
  • 异步回调

RabbitMQ 丢失

  • 开启持久化到 Disk
  • 宕机恢复会主动读取之前存储的数据
    • 创建 queue 设置为持久化
    • 发送消息设置为持久化
  • 结合 Producer 的 confirm 机制,完成持久化才返回 ack

Consumer 丢失

  • 关闭 RabbitMQ 自动 ack
  • Consumer 处理完消息主动调用 api

总结

image-20230723172829720

Kafka

Consumer 丢失

  • 消费到消息自动提交了 offset
  • 关闭自动提交 offset ,处理完成手动提交 offset
  • 处理完没提交 offset,保证重复消费的幂等性

Kafka 丢失

leader 宕机,其他 follower 刚好有些数据还没同步

  • topic 设置 replication.factor 每个 partition 必须有至少2个副本
  • kafka 服务端设置 min.insync.replicas,leader 感知到至少有一个 follower 还跟自己保持联系,确保 leader 挂了还有一个 follower
  • producer 设置 acks=all,要求每条数据必须写入所有 replica 之后,才认为写成功
  • producer 设置retries=MAX,要求一旦写入失败,就无线重试

顺序性

顺序错乱的场景

RabbitMQ

一个 queue,多个 consumer

image-20230723235424660

Kafka

  • 一个 topic,多个 partition。producer 生产消息指定key,将该相关的数据都分发到同一个 partition ,保证这个 partition 的数据是有序的。

  • consumer 消费拉取数据是有序的,处理数据可能使用多线程并发,也可能导致顺序错乱

image-20230723235759174

解决方案

RabbitMQ

  • 拆分多个 queue,每个 queue 对应一个 consumer
  • consumer 内部使用内存队列排队,分发给底层不同的 worker 来处理

image-20230724000027273

Kafka

  • 一个 topic,一个 partition,一个 consumer,内部单线程消费,吞吐量太低,一般不采用
  • 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;对于 N 给线程,每个线程分别消费一个内存

image-20230724000302673

延时&过期失效

  • 如何解决消息队列的延时以及过期失效问题?

  • 消息队列满了以后该怎么处理?

  • 有几百万消息持续积压几小时,怎么解决?

消息积压

  • 修复 consumer 的故障,确保恢复消费速度,将现有 consumer 停掉
  • 新建 topic,partition是原来的10倍,临时建立好原先10倍的 queue 数量
  • 临时分发数据的 consumer 程序,部署消费积压的数据,直接轮询写入临时创建的10倍数量的 queue
  • 接着征用10倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据
  • 等快速消费完积压数据后,恢复原先部署的架构

消息过期

RabbitMQ 可以设置过期时间,积压超过 TTL 就会清理掉,手动补偿丢失的数据

消息溢出

消息积压导致 mq 都快写满了,要么写临时程序接入数据快速消费,要么利用空闲时间再补偿丢失的数据

架构设计

  • 可伸缩性:快速扩容,增加吞吐量和容量,broker -> topic -> partition,每个 partition 放在一个机器,就存一部分数据。如果资源不够,给 topic 增加 partition ,然后做数据迁移,增加机器
  • 持久化:磁盘顺序读写
  • 可用性:多副本 -> leader & follower -> broker 挂了重新选举 leader 既可对外服务
  • 数据0丢失:replica、follower 个数,consumer 的 retry,ack
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奥库甘道夫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值