秒杀业务

年底了,没什么东西可以研究了,花点时间写一篇我们常见的秒杀业务。

秒杀就是在某一个时间点,大量用户在那几秒的时间内,一起来抢只有几件库存的商品,在前期为了业务系统整体的稳定性,我们最开始就需要把这块业务独立处理,它拥有自己的硬件资源,以免对现有的业务造成影响。

同时,我们的秒杀业务一开始也不会很频繁,前期我们会采用一个手动创建、扩容的方式来支撑,这需要和运营同事进行配合,提前告诉我们秒杀的活动规划,然后我们提前准备好相关的资源,秒杀结束后,我们再进行资源的回收。

1
2
3
4
5
6
7
8
9
flowchart TD
    OP{运营} --> |创建/编辑| ACTIVITY[活动]
    OP --> |通知活动计划| DEV{研发}
    OP --> |通知活动结束| DEV{研发}
    DEV --> |手动| CLOUD{云服务商}
    CLOUD --> |创建/删除| SLB[负载均衡]
    CLOUD --> |创建/删除| SERVER[服务器]
    CLOUD --> |创建/删除| MYSQL[数据库]
    CLOUD --> |创建/删除| REDIS[缓存]

好了,现在运营已经创建好了活动,并且通知到了我们,我们在活动开始的前一小时已经准备好了相关资源,这里时间要根据你的经验来,同时请给自己留点紧急处置时间。

服务器我推荐你购买抢占式实例,不管是哪个云,现在都有提供的。另外 2024 了,如果你的新业务还不支持容器化方式部署,我这里强烈推荐,经过我们的测试,我们从创建一台新服务器到业务部署可用,大概只需要两分钟(我们已经全面自动化了)。

推荐写的一个小工具,查询阿里云国内地域抢占式的实时价格: https://www.hongfs.cn/tools/aliyun_spot_price.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
flowchart TD
    USER1[用户] --> SLB{负载均衡}
    USER2[用户] --> SLB{负载均衡}
    USER3[用户] --> SLB{负载均衡}
    USER4[用户] --> SLB{负载均衡}
    SLB --> SERVER1[服务器1]
    SLB --> SERVER2[服务器2]
    SLB --> SERVER3[服务器N]
    SERVER1 --> MYSQL0[订单数据库]
    SERVER2 --> MYSQL0
    SERVER3 --> MYSQL0
    SERVER1 --> MYSQL1[秒杀数据库]
    SERVER2 --> MYSQL1
    SERVER3 --> MYSQL1
    SERVER1 --> REDIS1[秒杀Redis]
    SERVER2 --> REDIS1
    SERVER3 --> REDIS1
    ADMIN{管理端} --> MYSQL1
    ADMIN{管理端} --> REDIS1

上面是用户的请求流程,因为秒杀流量会比较多,不能只让一台服务器去承接,需要搭配负载均衡进行使用;如果你预估请求流量不高,我还是希望你前面也套一层云服务商的负载均衡,这块成本不高的,也方便后面加机器。

秒杀最重要还是商品不能超售,这块常规方案都是把库存数量缓存到 Redis 中,通过 Lua 脚本方式进行读取扣减,这是一个原子性的操作,Redis 的性能还是非常强的,10W QPS 应该不是问题,这块没必要集群部署。要提前缓存库存的原因,最好明确下这个库存是怎么写入的,比如运营创建、编辑秒杀活动时会异步写入,最好限制下活动开始前多久到活动结束前,禁止修改库存。

1
2
3
flowchart TD
    OP{运营} --> |创建/编辑| ACTIVITY[活动]
    ACTIVITY --> |监听存在后,写入库存信息| REDIS[秒杀Redis]

秒杀之前,除了你需要缓存库存,还需要把商品的信息也缓存起来,最好用户请求这块是直接走的 CDN,你也可以让服务器去承担,因为活动开始前,用户是逐步进来的,这块压力不大,但服务器的流量会比 CDN 贵非常多。返回给用户的秒杀数据,需要加上活动开始的时间,额外提供一个接口获取服务器现在的时间(如果使用本地时间,有人会改时间的),等待活动开始才激活用户的抢购按钮,前端要做好点击的限流处理。

用户秒杀过程中,如果你发现有些恶意请求,你可以直接打开云负载均衡中的 WAF 功能进行拦截。

1
2
3
4
5
6
flowchart TD
    USER1[用户] --> WAF{WAF}
    USER2[用户] --> WAF
    USER3[用户] --> WAF
    USER4[用户] --> WAF
    WAF --> SLB{负载均衡}

秒杀大家都特别喜欢玩随机拒绝,我们有另外一个操作,阿里云的 CDN 有个 EdgeScript 功能,可以在这里编写我们的一些简单脚本,,比如我们会配置一个比例,在范围内我们会生成一个签名,有这个签名才能去访问秒杀接口,相当于拒绝这一层不需要自有服务器处理,成本是非常低的。

用户秒杀成功后,订单信息你也直接写入到业务数据库中,量大就需要通过队列方式来写入,减少影响正常业务。

1
2
3
flowchart TD
    SERVER1[秒杀服务器] --> MQ{消息队列}
    MQ --> |消费| SERVER2[业务服务器]

用户也可能秒杀到了,又不付款,那就需要准备好一个脚本了,按照一定的频率,把超时未付款的订单进行关闭,然后将库存用 Lua 保证原子性的方式进行追加

好了,这就是我们的秒杀业务处理,如果你要上线相关的,记得要压测。

往上