本质是将同步处理转化成异步处理。
Pros:
1.可在模块、服务、接口等不同粒度上实现解耦
2.订阅/消费模式也可在数据粒度上解耦
3.可提高系统的并发能力,集中力量办大事(同步部分),碎片时间做小事(异步部分)
4.可提高系统可用性,因为缓冲了系统负载
Cons:
1.降低了数据一致性,如要保持强一致性,需要高代价的补偿(如分布式事务、对账)
2.有数据丢失风险,如宕机重启,如要保证队列数据可用,需要额外机制保证(如双活容灾)
总体来说,消息队列的适用场景还是很多的,如秒杀、发邮件、发短信、高并发订单等,不适合的场景如银行转账、电信开户、第三方支付等。关键还是要意识到消息队列的优劣点,然后分析场景是否适用则会水到渠成。
哪些问题适合使用消息队列来解决:
- 异步处理
- 可以更快的返回结果
- 减少等待,实现了步骤之间的并发,提升了系统性能
- 流量控制
- 消息队列可以隔离网关和后端服务,达到流量控制和保护后端服务的目的
- 增加了系统调用链,导致总体响应时间变长
- 异步消息调用增加了系统的复杂度
- 服务解耦
- 通过消息队列来实现各个系统间的通信,降低了耦合
各个消息队列的特点:
RabbitMQ:
优点:轻量,迅捷,容易部署和使用,拥有灵活的路由配置
缺点:性能较差,消息堆积时性能急剧下降,难于扩展
每秒钟处理几万到几十万条消息
RocketMQ:
优点:性能好,稳定可靠,有活跃的中文社区,延时优化好,毫秒级响应
缺点:与周边生态系统的兼容性较差
每秒钟处理几十万条消息
Kafka:
优点:大量使用了批量和异步的思想,拥有强大的性能及吞吐量,兼容性很好
缺点:由于“攒一波再处理”导致延迟比较高
每秒钟处理几十万条消息
WindQ:
1) 提供队列、主题两种消息服务;
2) 基于JMS协议;
3) MQ Server节点分布式部署及自动发现;
4) 消息发送和接收的负载均衡 ;
5) 消息发送的容错重试机制;
6) 消息发送的熔断;
7) 消息发送的TPS流控;
8) 在线动态扩缩容;
9) 多活场景支持,消息跨机房传输;
不支持:
不支持分布式事务
不支持顺序消息
不支持重复消息
只支持 JMS1.1 规范
极端情况下会丢失消息:server 宕机或磁盘损坏,堆积的消息会丢失
RabbitMQ 消息模型:
使用队列,通过 exchange 配置的策略来决定将消息投递到哪个或者多个队列中,这样也可以实现一对多的效果
RocketMQ 消息模型:
使用主题,标准的发布-订阅模式。
消息队列的“请求——确认”模式导致同一时间只能有一个消费者实例进行消费,否则就会出现消息空洞。
为了实现并发,RocketMQ 的每个主题包含了多个队列,来实现多实例并行生产和消费。
每个消费组中的consumer 是竞争关系
一条消息可以被不同的消费组消费
每个消费组在每个队列上维护了一个 offset 消费位置,这个位置之前的消息都被消费过,之后的都没有被消费国。
RocketMQ 的分布式事务:
1、消息队列开始事务,业务系统Producer 向消息服务器发送一个半消息,在事务提交之前对消费者不可见
2、业务系统执行本地事务
3、根据本地事务的结果来提交或者回滚消息队列的事务
也就是说业务系统会向消息队列提交两次请求,一次是发送半消息,一次是提交或者回滚事务
RocketMQ 的事务反查机制:
当第二次事务提交异常时,Broker 没有收到提交或者回滚的请求,那么 Broker 会定期去 Producer 上反查这个事务对应的本地事务的状态,然后根据反查结果来决定进行提交还是回滚。
所以业务代码需要实现一个反查本地事务状态的接口,告知 RocketMQ 本地事务是成功还是失败。
处理重复消息:
用幂等来解决
- 利用数据库唯一约束实现幂等
- 为更新的数据设置前置条件
- 利用分布式事务和分布式锁,给消息添加记录并检查操作
保证消息不会丢失:
在生产阶段,你需要捕获消息发送的错误,并重发消息
在存储阶段,你可以通过配置刷盘和复制相关的参数,让消息写入到多个副本的磁盘上,来确保消息不会因为某个 Broker 宕机或者磁盘损坏而丢失。
在消费阶段,你需要在处理完全部消费业务逻辑之后,再发送消费确认。
Q1:rocketmq,一个消费组在一个主题下的多个队列并发消费就无法保证消息的顺序性。这种该如何处理?
A1:按照订单ID或者用户ID,用一致性哈希算法,计算出队列ID,指定队列ID发送,这样可以保证相同的订单/用户的消息总被发送到同一个队列上,就可以确保严格顺序了。
Q2:客户端和mq要保持一种重试的机制,如果在网络延迟出现问题的时候,前面的消息一直未收到ack响应,若不做任何处理,后面的就会阻塞,还是重试之后放弃,若是不能发生丢失的信息该如何处理。
A2:会有一个超时,超时之前会阻塞,超时之后就解除锁定,允许其他消费者来拉消息,由于消费位置没变,下次再有消费者来这个队列拉消息,返回的还是上一条消息。
Q3:如何保证消息的唯一性,在重试的过程中,第一条消息已经发送,未收到ack,则进行第二次重试。此时网络故障恢复,则客户端会收到两条消息,客户端如何保证消息的唯一性。
一、如何预防消息积压?
1、发送端提高并发及批量大小;
2、消费端增加实例且同步扩容分区数量(队列数量);确保 consumer 数量和分区数量相等
二、如何处理消息积压?
1、消费端扩容;
2、服务降级;
3、异常监控。