消息队列 - 基础篇
消息队列 - 基础篇
前言
常见问题:
- 技术选型
- 高可靠、高可用、高性能
- 不重复、不丢失
- 水平扩展
底层技术:
- 分布式系统
- 海量数据
- 海量并发
消息模型
消息模型:
- 队列模型
生产者 - 队列 - 消费者
- 发布订阅模型
发布者 - 主题 - 订阅者
RabbitMQ 消息模型:
- Queue - Consumer
Producer - Exchange
- Queue - Consumer
RocketMQ / Kafka 消息模型:
- Queue/Partition -
Producer Group - Topic - Consumer Groups
- Queue/Partition -
- 一个主题可以有多个分区,以支持多实例并行生产和消费
- 一个主题可以被多个消费者组订阅,每个消费者组都会接收全量消息,同一个消息只能被组内的一个消费者消费
- RocketMQ 只在队列上保证消息的有序性,无法在主题层面保证消息的严格顺序
kafka 叫分区,RocketMQ 叫队列
消息丢失
消息丢失检测
- 分布式链路追踪系统
- 给消息添加序号
序号检测方式:
- 在生产者和消费者的拦截器注入和验证序号的连续性
- 确保生产者、消费者、消息队列分区的数量一致
消息可靠传递
消息传递的三个阶段:
- 生产阶段 (Producer)
- 存储阶段 (Broker)
- 消费阶段 (Consumer)
生产阶段:
- 消息队列通过请求确认机制和失败重试机制确保消息不丢失
- 生产者要确保处理消息发送失败的异常
存储阶段:
- 写入磁盘并且刷盘后再发送确认
- 集群环境要确保消息复制到两个副本后到发送确认
消费阶段:
- 消息队列通过请求确认和失败重试确保消息不丢失
- 消费者要在执行消费逻辑后再发送确认
消息重复
服务质量标准
MQTT 协议的三种服务质量标准:
at most once
:至多一次,允许丢失消息。at least once
:至少一次,不允许消息丢失,但允许消息重复。exactly once
:精确一次,不允许消息丢失,也不允许消息重复。
大多数消息队列产品都提供至少一次的服务质量标准。
用幂等性解决消息重复
处理消息重复,需要保证消费者的消费逻辑具有幂等性。
at least once + 幂等消费 = exactly once
幂等性操作:一个操作执行任意多次和执行一次对系统的影响是相同的。
常用的幂等设计:
- 利用数据库的唯一约束或
SETNX
实现幂等 - 增加一个版本号字段
- 使用 token 或 GUID (全局唯一ID),在操作前检查 token 是否有效,操作后销毁 token
检查、操作、销毁需要保证原子性和同步。
避免出现 A 检查后未销毁时 B 检查通过导致重复操作。
消息积压
Producer 性能
- 批量发送:适合离线业务,比较在意吞吐量的场景
- 提高并发:适合在线业务,比较在意时延的场景
Consumer 性能
- 优化消费业务逻辑
- 扩容 Consumer 实例数量(同时扩容分区数量)
要保证 Consumer 数量和分区数量一致,
因为每个分区实际上只支持单线程消费。
消息积压排查
排查方法:
- 监控系统
- 日志
常见原因:
- 突发请求导致生产者发送大量消息(服务降低、扩容消费者)
- 消费者消费失败导致重复消费
- 消费者发生死锁
分布式事物
分布式事务:
- 2PC:二阶段提交
- TCC(Try-Confirm-Cancel)
- 事务消息
事务消息
事务消息适合需要异步更新数据,对数据实时性要求不高的场景。
事务消息执行过程:
- 生产者在消息队列开启事务
- 生产者发送半消息(消息对消费者不可见)
- 生产者执行本地事务(数据库事务)
- 提交/回滚消息队列事务(提交后消息对消费者可见)
提交失败:
- kafka 在事务提交失败时会抛出异常。
- 重试:尝试再次提交事务
- 补偿:删除插入数据
- RocketMQ 使用事务反查机制来处理提交失败
参阅
- 《消息队列高手》- 李玥