0x0 消息队列场景
消息队列是我们最常听到的中间件,但是为什么我们需要消息队列,以下提供一些场景思考:
- 用户购物希望得到更快的响应,但是购物流程需要调用许多API,每个API都存在延时,最终响应时间让用户感觉很长。
- 我们不需要实时查看日志,但是高并发下大量日志处理需要较大线程开销,这个优先级很低,我们不希望挤占高优先级操作。
- 双十一有许多用户都在等待秒杀活动,QPS通常是主程序无法负载的,但我们不希望高QPS下主程序崩溃,我们只能在尽量不引发崩溃的高QPS的情况下处理订单,同时不希望丢失用户的订单请求。
- 用户注册流程需要发送验证码,写数据库,但是如果流程是同步的,那么如果数据库临时宕机,用户的请求将一直丢失。
0x1 消息队列优缺点
以上三个场景都可以使用消息队列解决,也即是消息队列的优点。
- 快速响应:假设完成所有流程再返回给用户的耗时是1000ms,添加请求到MQ需要50ms,不使用MQ流程完成再返回响应用户能清晰感知到延迟,但是将请求加入到MQ就响应给用户,则用户端只有50ms的延迟。
- 日志处理:如果我们在流程的每个操作都需要处理一次日志,进行日志解析等低优先级操作,当QPS很高的时候,这种开销就成为了不可忽视的问题,因此使用MQ将日志放入消息队列,再在适当的时机对日志进行处理,即将日志处理延迟。
- 削峰填谷:主程序无法处理过多的请求,则将请求先加入MQ,主程序从MQ中消费能够处理的量的请求,这样主程序不会因为高QPS而崩溃。
- 应用解耦/异步处理:为了让用户注册不会因为数据库宕机(或发送验证码系统)而无法提交请求,我们可以将发送验证码和写数据库变为异步操作,数据库系统从MQ消费验证成功Message后写数据库,验证码系统从MQ中消费注册请求Message后发送验证码,验证成功后向MQ生产验证成功Message。这样用户的注册请求就不会因为任意一个系统宕机而无法得到响应。
那么既然消息队列能解决这么多问题,缺点又在哪里呢?
- 可用性:我们可以注意到解决方案中我们都依赖于MQ,Message都在MQ里,那么如果MQ崩溃,所有系统都将不可用。
- 一致性:如果将流程异步,其中ABC都正常写入数据库,D失败了,那么这个操作就导致了数据不一致,但是我们已经响应了请求,在用户的视角他的请求已经完成。
- 可靠性:系统都认为MQ里的消息是它们需要处理的全部,但如果MQ发生了消息丢失的话,那么其他系统无法感知。
所以现在市场主流的消息队列都需要保证可用性和可靠性,一致性可以通过重新设计异步(部分操作使用同步)/提供回滚操作等方案去解决或者在MQ也提供监控等操作解决。