特性 | ActiveMQ | RabbitMQ | RocketMQ | kafka |
开发语言 | java | erlang | java | scala |
单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 |
时效性 | ms级 | us级 | ms级 | ms级以内 |
可用性 | 高(主从架构) | 高(主从架构) | 非常高(分布式架构) | 非常高(分布式架构) |
功能特性 | 成熟的产品,在很多公司得到应用; 有较好的文档; 各种协议支持较好 | 基于erlang开发,所以并发能力很强,性能及其好,延时很低; 管理界面较丰富 | MQ功能比较完善,扩展性佳 | 只支持主要的MQ功能,像一些消息查询,消息回溯等功能没有提供,毕竟是为大数据准备的,在大数据领域应用广 |
1.中小型公司首选RabbitMQ:管理界面简单,高并发
2.大型公司可以选择RocketMQ:更高并发,可对rocketmq进行定制化开发
3.日志采集功能:首选kafka,专为大数据准备
备注:
1)为什么要引入MQ这个组件?如何选型?
1-1)MQ引入:
消息队列(Message Queue,简称MQ)作为一种跨进程的通信机制,在分布式系统中被广泛应用。其主要作用包括但不限于:
- 解耦:通过消息队列,生产者和消费者之间不需要直接交互,降低了系统的耦合度。
- 异步处理:允许某些操作异步执行,从而减少响应时间,提高用户体验。
- 流量削峰:在高并发场景下,能够平滑瞬时高峰流量,保护后端服务不被压垮。
- 数据一致性与可靠性:确保消息的可靠传递,即使在系统故障的情况下也能保证数据最终一致性。
1-2)MQ选型:
选择消息队列产品时,通常会考虑以下几个因素:
- 功能特性:如消息持久化、消息过滤、事务支持等。
- 性能:包括消息吞吐量、延迟等。
- 可扩展性:是否容易水平扩展以应对更大的负载。
- 社区活跃度和支持:一个活跃的社区意味着更好的维护和技术支持。
- 易用性:API的友好程度、文档质量等。
- 与其他系统的集成能力:是否能很好地与其他已有系统或技术栈集成。
每个消息队列都有其特点和适用场景。比如RabbitMQ以其灵活性著称,Kafka适用于大规模数据流处理,RocketMQ则擅长于金融级交易处理。选择哪个MQ取决于具体的应用场景需求、团队的技术栈偏好以及对上述考量因素的权衡。
2)如何提升RocketMQ顺序消费性能?
将消息划分为主题组,不同消息推送至不同组,消费者按照按照不同组消费
优化方案:
2-1) 增加队列(MessageQueue)数量
原理:队列是并行消费的基本单位,队列数决定了最大并行度。
操作步骤:
生产者端:创建Topic时设置足够多的队列(如-n 16)。
消费者端:确保消费者实例数 ≤ 队列数(避免队列分配不均)。
最佳实践:
队列数 = 业务键分片数 × 预期冗余(如订单分1000个桶,队列数设为16~32)。
通过mqadmin updateTopic动态扩容队列(需重启生产者生效)。
2-2) 优化业务键分片策略
目标:将不同业务键的消息均匀分散到不同队列。
实现方式:
哈希分片:对业务键(如订单ID)计算哈希值,按队列数取模。
2-3) 消费者线程模型优化
默认限制:单个队列由单个线程串行消费。
优化方案:
多队列并行:增加消费者实例,确保每个队列有独立线程。
线程池处理:在消费者内部对非顺序依赖操作异步化。
2-4) 批量消费
原理:单次拉取多条消息,减少网络交互和线程调度开销。
配置方式:
修改消费者参数:consumeMessageBatchMaxSize=32(单批次最大消息数)。
生产者批量发送:send(msgList)。
注意事项:
确保批量处理逻辑的原子性(如批量更新数据库)。
2-5) 消费者参数调优
参数 | 建议值 | 说明 |
| 16~32 | 最小消费线程数(需 ≥ 队列数) |
| 32~64 | 最大消费线程数 |
| 32~64 | 单次从Broker拉取的消息数 |
| 3 | 失败消息最大重试次数,避免阻塞队列 |
3)如何确保你的消息只被消费一次
3-1)消息生产端:防止重复发送
1) 生产者幂等性
原理:确保同一条消息多次发送到队列时,仅生效一次。
实现方式:
唯一消息ID:为每条消息生成唯一标识(如业务ID + 时间戳)。
服务端去重:消息队列服务端记录已接收的消息ID,拒绝重复消息。
2) 事务消息
场景:生产者发送消息与本地事务绑定,确保消息发送与业务操作原子性。
3-2)消息队列服务端:去重与一致性
1) 服务端去重
实现:
Redis 缓存去重:记录已处理的消息ID,设置过期时间。
数据库唯一索引:将消息ID作为唯一键插入去重表。
3-3)消息消费端:幂等处理
1) 业务逻辑幂等性设计
去重机制:
唯一标识校验:消费前检查消息ID是否已处理。
状态机控制:基于业务状态拒绝重复操作(如订单已支付则跳过支付逻辑)。
2) 消费位移管理(这个应该算是消费者确认)
手动提交Offset:确保业务处理成功后提交Offset。
4)MQ消息队列消息堆积问题排查和解决思路
4-1)首先通过指标确认问题:
- 生产速率(Producer TPS):确认生产者是否突发流量激增。
- 消费速率(Consumer TPS):对比生产与消费速率,判断消费能力是否不足。
- 堆积量(Backlog):监控消息队列中未消费消息的数量。
- 消费延迟(Consumer Lag):如 Kafka 的 Lag 指标,反映消息处理延迟。
4-2)问题分析:
原因类型 | 具体场景 |
消费者能力不足 | 消费者实例少、单线程消费、业务逻辑耗时(如同步 IO 操作)。 |
生产者流量突增 | 大促活动、定时任务触发批量消息发送。 |
消费阻塞 | 消息处理异常(如死锁、依赖服务超时)、死信队列未处理导致重试卡死。 |
资源瓶颈 | CPU、内存、网络带宽不足,数据库连接池满载。 |
配置不合理 | 消费者并发参数(如线程数、Prefetch Count)、分区/队列数不足。 |
4-3)排查步骤:
4-3-1) 定位瓶颈方向:
生产 vs. 消费:
若 生产速率 > 消费速率:消费者能力不足或消费逻辑阻塞。
若 生产速率正常,消费速率骤降:检查消费者错误日志或资源瓶颈。
4-3-2) 分析消费者端:
日志检查:
频繁错误(如数据库连接失败、第三方 API 超时)。
长耗时操作(如未异步化的文件处理、复杂计算)。
资源监控:
CPU、内存使用率(如 top、jstat)。
线程状态(如 jstack 检查死锁或阻塞线程)。
数据库连接池使用率(如 Druid 监控)。
4-3-3) 检查消息队列配置:
分区/队列数:Kafka/RocketMQ 的分区数需 ≥ 消费者实例数。
消费者参数:
Kafka:max.poll.records(单次拉取消息数)、fetch.max.bytes。
RabbitMQ:prefetch_count(避免单通道过载)。
RocketMQ:consumeThreadMin、consumeThreadMax。
4-4)解决方案
4-4-1) 提升消费能力
水平扩容:
增加消费者实例(需确保分区/队列数足够,如 Kafka 分区数限制)。
示例:Kafka 分区数为 10,则消费者组最多 10 个实例并行消费。
垂直优化:
异步化处理:将耗时操作(如 IO、RPC)异步执行,释放消费线程。
批量消费:如 Kafka 配置 max.poll.records=500,单次处理多条消息。
并行处理:在消费者内部使用线程池(需保证消息顺序性允许)。
4-4-2) 解决消费阻塞
错误处理:
捕获异常并降级处理,避免无限重试。
设置合理的重试次数和死信队列(DLQ)。
依赖治理:
熔断降级:使用 Sentinel 或 Hystrix 熔断超时的外部调用。
超时配置:设置合理的 RPC 和数据库超时时间。
4-4-3) 流量控制
生产者限流:
降低生产者发送速率(如令牌桶算法)。
示例:Kafka Producer 配置 max.block.ms 和 batch.size。
队列降级:
丢弃非核心消息(如日志数据),保障核心业务。
动态开关:通过配置中心动态调整消费策略。
5)消息队列消息没有成功消费怎么办
5-1)原因分析与分类(首先需明确消息消费失败的原因):
- 临时性错误:网络抖动、依赖服务短暂不可用、并发锁冲突等。)
- 永久性错误:消息格式错误、业务逻辑缺陷、不兼容的版本升级等。
- 系统瓶颈:消费者处理能力不足、消息积压导致超时。
5-2)解决方法:
5-2-1) 自动重试机制
实现方式:
客户端重试:消费者捕获异常后,本地重试(如固定间隔、指数退避)。
服务端重试:利用消息队列的 重试队列(如 RocketMQ 的 %RETRY% Topic)自动重新投递。
适用场景:临时性错误(如网络抖动)。
5-2-2)永久性错误:死信队列功能:将多次重试仍失败的消息转移到独立的死信队列,供后续处理。
处理方式:
人工介入:检查错误日志,修复代码后重新投递。
自动修复:针对特定错误(如字段缺失)编写补偿任务。
适用场景:永久性错误或需人工干预的场景。
更多内容: