消息队列在发布/订阅模式中的作用是持久化缓冲(durable buffer),发布/订阅模式指的是发送方可以将消息异步发送给一个系统中的不同组件,而无需知道接收方是谁
使用消息队列的场景
1、异步处理
比如秒杀系统,包括风险控制; 库存锁定; 生成订单; 短信通知; 更新统计数据等,风险控制和库存锁定之后,其余的流程可以在后续再进行,也就是可以异步处理
好处:更快的返回结果;减少等待,实现了步骤流程间的并发,提升了系统性能
2、流量控制
比如秒杀系统,使用消息队列隔离网关和后端服务,以达到流量控制和保护后端的目的
这种设计的优点是:能根据下游的处理能力自动调节流量,达到“削峰填谷”的作用。但这样做同样是有代价的:
增加了系统调用链环节,导致总体的响应时延变长。
上下游系统都要将同步调用改为异步消息,增加了系统的复杂度。
如果我们能预估出秒杀服务的处理能力,就可以用消息队列实现一个令牌桶,更简单地进行流量控制。
3. 服务解耦
4、作为发布 / 订阅系统实现一个微服务级系统间的观察者模式;
5、连接流计算任务和数据;
6、用于将消息广播给大量接收者。
缺点:
引入消息队列带来的延迟问题;
增加了系统的复杂度;
可能产生数据不一致的问题。
消息队列的本质是将同步处理转成异步处理,异步会带来相应的好处,但也有弊端。
Pros:
1.可在模块、服务、接口等不同粒度上实现解耦
2.订阅/消费模式也可在数据粒度上解耦
3.可提高系统的并发能力,集中力量办大事(同步部分),碎片时间做小事(异步部分)
4.可提高系统可用性,因为缓冲了系统负载
Cons:
1.降低了数据一致性,如要保持强一致性,需要高代价的补偿(如分布式事务、对账)
2.有数据丢失风险,如宕机重启,如要保证队列数据可用,需要额外机制保证(如双活容灾)
总体来说,消息队列的适用场景还是很多的,如秒杀、发邮件、发短信、高并发订单等,不适合的场景如银行转账、电信开户、第三方支付等。
上文提到的限流算法,包括漏桶算法、令牌桶算法
https://blog.csdn.net/tianyaleixiaowu/article/details/74942405
https://www.cnblogs.com/cjsblog/p/9379516.html
几种主流的消息队列
1、RabbitMQ
支持非常灵活的路由配置,和其他消息队列不同的是,它在生产者和队列之间增加了Exchange模块,可以理解为交换机,根据配置的路由规则将生产者发出的消息分发到不同的队列中
存在的问题:
(1)对消息堆积支持的并不好,
(2)性能比较差,每秒处理几万到十几万条
(3)编程语言使用Erlang,小众语言
发布/订阅模式:消息队列在该模式中起到了持久化缓冲(Durable Buffer)的作用。发送方被称为发布者(Publisher),接收方被称为订阅者(SubScriber)
2、RocketMQ
(1)对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级响应,最大的优点
(2)性能比rabbitMQ高,每秒大概处理几十万条
3、kafka
(1)批量、异步、性能高
(2)每秒几十万条
问题:同步收发性能低,响应时间久,
在它的 Broker 中,很多地方都会使用这种“先攒一波再一起处理”的设计。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高。所以,Kafka 不太适合在线业务场景。
总结
如果说,消息队列并不是你将要构建系统的主角之一,你对消息队列功能和性能都没有很高的要求,只需要一个开箱即用易于维护的产品,我建议你使用RabbitMQ。
主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,那 RocketMQ 的低延迟和金融级的稳定性是你需要的。
处理海量的消息,像收集日志、监控信息或是前端的埋点这类数据,或是你的应用场景大量使用了大数据、流计算相关的开源产品,那 Kafka 是最适合你的消息队列。
如何保证消息的严格顺序?
主题层面是无法保证严格顺序的,只有在队列上才能保证消息的严格顺序。一种实现思路,在发送端,使用账户ID作为key,采用一致性hash算法计算出队列编号,制定队列发送消息,一致性hash算法可以保证,相同key的消息总是发送到同一个队列上
在生产者发送消息时,需要使用分片算法把消息均匀地分布到主题的所有分区(队列)上。如果需要保证 Key 相同的消息的严格顺序,并且能支持对分区数量进行水平扩容。
检索表算法:在检索表中存储 Key 和分区的对应关系,通过查表确定分区号。
一致性哈希算法