大家都知道在 Redis 集群部署模式出现之前,市面上大多数 的 Redis 都是采用一主多从模式,写操作全部是由主节点来执行。但是一主多从模式下主节点是单节点,所以 Redis 的写操作并发能力远远低于读操作并发能力。
那么在千万级并发流量场景下,虽然前面我们通过流量拦截器将大部分流量过滤掉了,但剩下的流量也不容小视。比如虽然过滤了 990 万,也就是 99% 的流量,但还剩下 10 万流量。如果这些流量都去扣减库存,会对 Redis 主节点将会产生巨大压力。怎么办呢?这就需要流量串行化。具体要怎么做呢?
总的来说,秒杀中的串行化主要是通过“队列”和“分布式事务”来实现的,具体分三步。
第一步,秒杀服务在扣减内存缓存中的库存成功后,将流量转入到它的内存队列中,进行初步排序,为写 MQ 做准备。
这里注意的是,要控制好内存队列缓冲区大小,太小可能会导致并发写入的时候大量请求被阻塞,可以将大小设置为消费端速度的两倍。比如消费端速度是 1000 QPS,则缓冲区可以设定为可以缓存 2000 个请求。
第二步,使用一个线程或者协程以固定速度从内存队列中消费流量,将流量写入到像 RabbitMQ 这种 MQ 中。
这一步主要是为了减轻 MQ 的并发压力,需要根据 MQ 的承载能力计算好速度。比如 MQ 的并发承载能力为 5 万 QPS,秒杀有 50 个节点,则每个节点的速度应当低于 1000 QPS。保留 20% 余量的话,每个节点的速度可以设定为 800 QPS。
第三步,使用另一个线程或协程,以低于第二步中的固定速度从 MQ 中消费流量,然后利用 Redis 事务从 Redis 中扣减库存,避免超售。最终,根据扣减库存的结果,给用户返回对应的提示信息。
需要注意的是,秒杀服务有多个节点,不同节点需要用使用不同的队列,但单个节点的内部,必须用同一个队列。这么做是因为多个节点间是不能共享客户端连接的,只有在节点内部消费自己的流量,才能给用户返回处理结果。
看到上面这三步,不知道你有没有联想到之前讲到的限流器?没错,第二步和第三步中的固定速度就是用限流器来实现。可以说,串行化的核心思路就是:使用队列将请求进行排队、限流,使用分布式锁对资源进行原子操作。
注意:本文归作者所有,未经作者允许,不得转载