【System Design】设计一个春晚红包雨系统

443 阅读4分钟

欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。

step 1:需求分析

假设春晚有企业向全国观众发送10亿红包。每个红包被抢10次,即100亿QPS。

image.png

step 2:确认边界与大致架构

这个场景可以使用5So反推边界。 因为是10亿红包雨,所以单体难以支撑,所以是分布式架构,所以要考虑CAP。 因为是红包,涉及金额,所以要考虑安全性。

step 3:详细设计

首先,详细设计,设计底层核心逻辑。

设计常规的红包操作

一个红包从发送到领取需要有哪些事务操作? image.png 其中,发红包是一个简单操作,红包要保障能长时间存在(一天),需要持久化保障: 发送红包成功=发送方扣除DB余额+DB增加红包

抢红包则是一个线程安全的原子操作: 获取红包 = 获取红包余额+计算红包额度+扣减红包余额 抢红包(原子操作)= 获取红包+增加余额 在抢红包时,操作需要满足数据的多线程安全。

  • 计算红包+扣减红包余额操作 image.png

考虑并发操作

常规设计对DB操作压力大,在红包雨场景下肯定无法保障红包雨抢红包的实时、高效。 因此,我们首先考虑减少DB压力。

  • 减少红包DB操作 红包雨特性为时间短。因此,在发送红包成功后(入DB),每次抢红包扣减红包额度的操作都不进行DB操作。 而是在内存中操作完成,红包雨时间结束后再行操作DB。

  • 缓冲用户DB操作 10亿红包意味着10亿余额写请求,DB压力大,实时性差。 所以使用消息队列做异步处理+保障可靠性,起到缓冲用户DB操作的效果。

  • 预计算 每次获取红包余额然后计算红包的方法会有一些资源消耗。 在内存中计算会快一些,但还是需要加锁扣减。 在高并发场景在可以通过预计算来避免,不再实时计算红包大小。

预计算详细方案

  • 算个10亿红包(全部预计算) 预计算10亿红包,为每个红包设立token(或者金额+唯一id),讲这些token存入高速、可以并发访问的存储中,如Redis。 用户抢红包时从Redis红包池获取红包。

  • 分段预计算+实时计算少量随机性(部分预计算) 不将所有小红包完全预拆分,而是将总红包金额和总数量加载到 Redis 中。使用lua脚本进行分段计算。

  • 中奖令牌+异步发放金额 预先生成“中奖令牌”存入Redis,抢红包时=抢令牌,然后拿着令牌异步计算红包金额。

预计算需要用Redis集群分散红包池,且需要负载均衡策略讲服务器请求分散到不同实例。用Redis了也可以多加一级本地缓存,每个应用实例缓存部分到本地,形成多级缓存。 不过多级缓存需要考虑本地缓存丢失情况,设计更复杂一些。

考虑可靠性与可用性

在解决了功能性问题后,我们需要考虑系统的可靠性。 100亿QPS瞬时压力多半超出系统处理能力。

网关限流与负载均衡

在网关对用户身份做校验与行为检测,对异常流量进行限制,,防机器人干扰用户抢红包。 同时做好路由,均衡负载请求压力。

柔性处理异常

服务异常情况做服务降级、快速响应,并设计服务集群熔断保护等。

幂等性设计

一个红包只有一份钱,这很好理解。多次点击一个红包结果应当是一样的。

数据一致性与对账对账

红包雨结束后的数据对账是非常重要的。所以需求每次红包记录,略...

考虑用户体验

公平设计

如果用户一直没有抢到红包肯定不好。要设计兜底机制,比如分出一批红包,专为手气差的用户准备。

step 4:架构设计

核心架构设计如下:

image.png