业务工作流,每个业务节点之间需要通信传输信息,但是每个节点的接受、发送速度以及使用这些信息的时间都各不相同。
以巧克力生产工作流为例:
传送带解决了半成品运输问题,仓库可以暂存一些半成品,解决了上下游速度不一致的问题。
消息队列的常用使用场景:
1、异步处理
秒杀系统中,大多需要使用到消息队列,而秒杀系统中核心问题是:尽可能多地处理短时间内的海量请求。
处理一个秒杀请求包含了很多步骤:
- 风险控制;
- 库存锁定;
- 生成订单;
- 短信通知;
- 更新统计数据
能否决定秒杀成功,实际上只有风险控制和库存锁定这 2 个步骤,只要用户的秒杀请求通过风险控制,并在服务端完成库存锁定,就可以给用户返回秒杀结果了,对于后续的生成订单、短信通知和更新统计数据等步骤,并不一定要在秒杀请求中处理完成。
所以当服务端完成前面 2 个步骤,确定本次请求的秒杀结果后,就可以马上给用户返回响应,然后把请求的数据放入消息队列中,由消息队列异步地进行后续的操作。
以用户注册过程为例
用户注册后,需要发注册邮件和注册短信
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
(3)引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下
消息队列对同步过程进行了优化,将业余中非核心、实时性一致性要求不高的部分,变成异步过程进行处理。加快处理速度,提高系统的并发能力
好处:更快地返回结果,减少等待,自然实现了步骤之间的并发,提高系统总体性能。
坏处:需要保证每个消息确实被后续步骤所消费,避免消息丢失。
2、流量控制
使用消息队列隔离网关和后端服务,以达到流量控制和保护后端服务的目标,后端服务以最大的处理能力处理请求。达到了“削峰填谷”的作用。
100万用户在高峰期的时候,每秒请求有5000个请求左右,一般的MySQL每秒钟扛住2000个请求就不错了,如果达到3000个请求的话可能MySQL直接就瘫痪了。
对于超时的请求可以直接丢弃,APP 将超时无响应的请求处理为秒杀失败即可。随时增加秒杀服务的实例数量进行水平扩容,而不用修改其他部分。
坏处:增加了系统调用链环节,导致总体的响应时延变长。上下游系统都要将同步调用改为异步消息,增加了系统的复杂度。
如果能预估出秒杀服务的处理能力,则可以使用消息队列实现一个令牌桶,更加简单地进行流量控制。
令牌桶控制流量的原理是: 单位时间内只发放固定数量的令牌到令牌梗中,规定服务在处理请求之前必须先从令牌桶中拿出一个令牌,如果令牌桶中没有令牌,则拒绝请求。这样就保证单位时间内,能处理的请求不超过发放令牌的数量,起到了流量控制的作用。
令牌桶可以简单地用一个有固定容量的消息队列加一个“令牌发生器“来实现: 令牌发生器按照预估的处理能力,匀速生产令牌并放入令牌队(如果队列满了则丢弃令牌) ,网关在收到请求时去令牌队列消费一个令牌,获取到令牌则继续调用后端秒杀服务,如果获取不到令牌则直接返回秒杀失败。
3、服务解耦
所有的电商都选择用消息队列来解决类似的系统耦合过于紧密的问题。引入消息队列后,订单服务在订单变化时发送一条消息到消息队列 的一个主题 Order 中,所有下游系统都订阅主 题 Order, 这样每个下游系统都可以获得一份实时完整的订单数据。
无论增加、减少下游系统或是下游系统需求如何变化,订单服务都无需做任何更改,实现了订单服务与下游服务的解耦。
4、分布式事务
通过消息中间件,来保证分布式事物的最终一致性。这是一种比较提倡的解决方案,有利于服务之间解耦,虽然是异步的,但基本上事物也是即时性的。
fe:将业务A的提交操作和发送消息的记录 写在同一个事物中(把消息发送记录在本地库中),然后业务B接收到消息之后进行相应的操作,成功后,向A发送成功通知。失败则通知A,进行回滚。 当A中的消息发送不成功的时候,则会通过定时任务轮训来进行发送,从而保证消息最终肯定可以到达B。 但是当A接收到B的成功通知后 应该删除消息记录或者修改记录状态,但是突然A挂掉了,A就没有及时修改本地消息记录,导致定时轮训的时候会重发,如果此时B中业务不是幂等的则会出现问题,所以如何避免这种消息重发呢? 在B方也对消息进行记录,当A中发送过来消息后,B判断消息是否执行过,来避免这种问题。
Pros: 解耦、异步、削峰填谷
1.可在模块、服务、接口等不同粒度上实现解耦
2.订阅/消费模式也可在数据粒度上解耦
3.可提高系统的并发能力,集中力量办大事(同步部分),碎片时间做小事(异步部分)
4.可提高系统可用性,因为缓冲了系统负载
Cons:
1.降低了数据一致性,如要保持强一致性,需要高代价的补偿(如分布式事务、对账)
2.有数据丢失风险,如宕机重启,如要保证队列数据可用,需要额外机制保证(如双活容灾)
3.系统的复杂性提高。引入了MQ之后,需要考虑的问题也变得多了,如何保证消息没有重复消费?如何保证消息不丢失?怎么保证消息传递的顺序?
4.一致性问题。A系统发送完消息直接返回成功,但是BCD系统之中若有系统写库失败,则会产生数据不一致的问题。
总体来说,消息队列的适用场景还是很多的,如秒杀、发邮件、发短信、高并发订单等,不适合的场景如银行转账、电信开户、第三方支付等。关键还是要意识到消息队列的优劣点,然后分析场景是否适用则会水到渠成。
参考:
为什么需要消息队列——李玥
来源:CSDN
作者:码天码地
链接:https://blog.csdn.net/u013140345/article/details/104159729