高并发系统:消息队列使用

在历年的工作经历中,一直把消息队列看作暂时存储数据的一个容器,认为它是一个平衡低速系统和高速系统处理任务时间差的工具。
我理解的消息队列在高并发系统设计中起到的作用的主要有以下三点:

  • **削峰填谷:**可以削去到达秒杀系统的峰值流量,让业务逻辑的处理更加缓和,但会造成请求处理的延迟;
  • **异步处理:**可以简化业务流程中的步骤,提升系统性能,但是你需要分清同步流程和异步流程的边界;
  • **解耦合:**可以将秒杀系统和数据系统解耦开,这样两个系统的任何变更都不会影响到另一个系统。

以秒杀场景为例:

削峰填谷

在秒杀场景下短时间之内数据库的写流量会很高,但高并发的写请求并不是持续的,也不是经常发生的,而只有在秒杀活动开始后的几秒或者十几秒时间内才会存在。在数据库层面下功夫来提高性能有点得不偿失。

**所以我们的思路是:**将秒杀请求暂存在消息队列中,然后业务服务器会响应用户“秒杀结果正在计算中”,释放了系统资源之后再处理其它用户的请求。
削峰填谷

我们会在后台启动若干个队列处理程序消费消息队列中的消息,再执行校验库存、下单等逻辑。因为只有有限个队列处理线程在执行,所以落入后端数据库上的并发请求是有限的。而请求是可以在消息队列中被短暂地堆积,当库存被消耗完之后,消息队列中堆积的请求就可以被丢弃了。

这就是消息队列在秒杀系统中最主要的作用:削峰填谷,也就是说它可以削平短暂的流量高峰,虽说堆积会造成请求被短暂延迟处理,但是只要我们时刻监控消息队列中的堆积长度,在堆积量超过一定量时,增加队列处理机数量来提升消息的处理能力就好了,而且秒杀的用户对于短暂延迟知晓秒杀的结果也是有一定容忍度的。

这里需要注意一下,我所说的是“短暂”延迟,如果长时间没有给用户公示秒杀结果,那么用户可能就会怀疑你的秒杀活动有猫腻了。所以在使用消息队列应对流量峰值时,需要对队列处理的时间、前端写入流量的大小、数据库处理能力做好评估,然后根据不同的量级来决定部署多少台队列处理程序。

异步处理

其实在大量的写请求“攻击”你的电商系统的时候,消息队列除了发挥主要的削峰填谷的作用之外,还可以实现异步处理来简化秒杀请求中的业务流程,提升系统的性能。

异步处理

你想,在刚才提到的秒杀场景下,我们在处理购买请求时需要 500ms。这时你分析了一下整个的购买流程,发现这里面会有主要的业务逻辑,也会有次要的业务逻辑:比如说,主要的流程是生成订单、扣减库存;次要的流程可能是我们在下单购买成功之后会给用户发放优惠券,会增加用户的积分。

解耦合

除了异步处理和削峰填谷以外,消息队列在秒杀系统中起到的另一个作用是解耦合。

比如数据团队对你说,在秒杀活动之后想要统计活动的数据,借此来分析活动商品的受欢迎程度、购买者人群的特点以及用户对于秒杀互动的满意程度等等指标。而我们需要将大量的数据发送给数据团队,那么要怎么做呢?

一个思路是:使用 HTTP 或者 RPC 的方式来同步地调用,也就是数据团队这边提供一个接口,我们实时将秒杀的数据推送给它,但是这样调用会有两个问题:

  • 整体系统的耦合性比较强,当数据团队的接口发生故障时,会影响到秒杀系统的可用性。
  • 当数据系统需要新的字段,就要变更接口的参数,那么秒杀系统也要随着一起变更。

这时,我们可以考虑使用消息队列降低业务系统和数据系统的直接耦合度。

秒杀系统产生一条购买数据后,我们可以先把全部数据发送给消息队列,然后数据团队再订阅这个消息队列的话题,这样它们就可以接收到数据,然后再做过滤和处理了。

秒杀系统在这样解耦合之后,数据系统的故障就不会影响到秒杀系统了,同时当数据系统需要新的字段时,只需要解析消息队列中的消息,拿到需要的数据就好了。

系统解耦

总结

消息队列在高并发系统设计中起到的作用以及一些注意事项,重点如下:

  • 削峰填谷是消息队列最主要的作用,但是会造成请求处理的延迟。
  • 异步处理是提升系统性能的神器,但是你需要分清同步流程和异步流程的边界,同时消息存在着丢失的风险,我们需要考虑如何确保消息一定到达。
  • 解耦合可以提升你的整体系统的鲁棒性。