消息队列作用

Friday, August 2, 2019

为什么需要消息队列

我们知道,消息队列的主要功能,就是解决应用间的通信问题,但是消息队列并不仅限于次。因为不同的应用,处理消息的效率是不一致的,这会出现消息处理不及时或者说消息堆积的问题,此时,消息队列还会配对类似临时仓库的东西,起到通信过程中缓存的作用。

如上图,这里的传送带,就类似消息队列的作用。

哪些问题适合使用消息队列

异步处理

比如我们常见的秒杀系统,这个系统需要解决的核心问题是,如何利用有限的服务器资源,尽可能地短时间内处理海量请求。

一个秒杀系统可能包含如下步骤

  • 风险控制
  • 库存锁定
  • 生成订单
  • 短信通知
  • 更新统计数据

假如按照正常流程,那就是前端发送请求给网关,依次调用上述流程,然后返回结果。 我们看到上述的步骤中,对于是否秒杀成功起决定作用的,其实是风险控制和库存锁定这两个步骤。我们可以说,只要这个用户通过风险控制,并且锁定了库存,其实就可以给用户返回秒杀结果了。后续的步骤,并不一定要在此时此刻就处理完成。

所以我们可以异步的处理上述步骤,如下图所示。

这么做的好处显而易见:

  • 可以更快地返回结果
  • 减少等待,自然实现了步骤之间的并发,提升系统总体性能

流量控制

秒杀系统还有一个关键问题,就是流量控制,避免短时间内大量的请求,压垮服务器。

一个好的程序,应该可以在海量的请求下,尽可能的处理更多的请求,拒绝处理不了的请求以保证自身运行正常,但是现实并不是这么多程序都能设计的这么好,而且直接拒绝返回请求错误,对用户体验也不怎么好。

我们可以使用消息队列,隔离网关和后端服务,以达到流量控制和保护后端服务的目的。如下图

这样的话,大量请求到了网关之后,并不会直接冲击后台服务,而是堆积在消息队列中,后端按照自己的最大处理能力,从消息队列中消费请求进行处理。

对于超时的请求可以直接丢弃,前端将超时无响应的请求处理为秒杀失败即可。

这种设计,达到了削峰填谷的作用,但是。

  • 增加了系统调用环节,导致总体响应时间变长
  • 上下游系统都要将同步调用改为异步消息,增加复杂度。

那可以不可以优化呢,假如我们可以预估秒杀服务的处理能力,就可以使用消息队列实现一个令牌桶,更简单地进行流量控制。

原理是:单位时间内只发放固定数量的令牌到令牌桶中,规定服务在处理请求之前必须先从令牌桶拿出一个令牌,如果令牌桶没有令牌,则拒绝请求。这样就能起到流量控制的作用。

只需在网关处理请求时增加一个获取令牌的逻辑。令牌桶可以简单地用一个固定容量的消息队列加一个令牌发生器来实现。发生器按照预估的处理能力,匀速生产令牌并放入令牌队列,网关收到请求时消费一个令牌,获取到令牌则继续调用后端秒杀服务,获取不到则直接返回秒杀失败。

服务解耦

其实从上面的案例,我们已经可以看出,消息队列可以用来服务解耦。比如一个电商系统,当一个新订单创建时:

  • 支付系统需要发起支付流程
  • 风控系统需要审核订单的合法性
  • 客服系统给用户发短信
  • 经营分析系统需要更新统计数据
  • 其他系统

这些订单的下游系统,都需要实时的获取订单数据。业务发展越大,下游系统就越多,并且每个系统可能只需要订单数据的一个子集,负责订单系统的开发不得不花费很大精力,应对不断变化的下游系统,不断的更改接口,任何一次更改,都需要重新部署上线。

引入消息队列后,订单服务在订单变化时发送一个消息到消息队列的一个主题Order中,所有下游系统都订阅主题Order,这样每个下游系统都可以获得一份实时完整的订单数据。这样就实现了订单系统给与下游服务的解耦。

消息队列

常见的消息队列产品

网络协议通信流程