业务场景
分3个过程:
1. 发红包
- 设置红包金额、数量
- 从用户账号中扣除金额
- 生成红包、发送抢红包链接
2. 抢红包
- 用户点击抢红包链接
3. 拆红包
- 用户拆红包
- 修改红包剩余金额、剩余数量-1
- 用户抢到红包用户的账户余额
架构分析
难点在于:大访问高并发。解决方法:
- 请求过滤。这是因为:只有少数人可以抢到红包,大部分的请求都属于无效请求,因此要把大量无效的请求挡在外面。
- 使用缓存。红包本身是一个临时性的东西,因此可以放在缓存中,同时缓存的访问速度快。
架构设计
- 数据库:mysql + redis
mysql数据库结构:
红包ID、红包剩余金额/个数、过期时间、发送红包的用户ID、创建时间、总金额/个数、过期时间、成功抢到红包的用户信息
redis结构:
抢红包请求队列(队列)
拆红包请求队列(队列)
红包剩余金额/个数(键值对)
已抢到红包的用户信息(有序集合)之所以分成两个请求队列,是因为抢红包与拆红包之间存在一定间隔,先抢到红包的用户,并不一定先拆开红包。
拆红包
能成功进入抢红包请求队列的用户,需满足一下两个条件:
1)红包的剩余数量 > 0
2)用户不在“成功抢到红包的用户信息”中存在一个后端进程,负责从“抢红包请求队列”取出请求,生成并返回用于一个响应参数:Token。
拆红包(请求参数:红包ID + Token)
1)请求被放至“拆红包请求队列”中。
2)存在一个后端进程,负责从队列中取出请求。判断红包剩余数量是否大于0,红包ID与Token是否合法等校验后,生成红包金额,并更新redis中红包剩余金额/数量和已抢到红包的用户信息。
3)通过异步方式更新mysql。
红包算法
红包金额不是预分配的,而是拆红包时实时计算出来的。具体是在0.01 ~ 剩余平均值(剩余金额/剩余数量)*2 之间的一个随机数。
效果
假设发了一个红包,分为5份。现在有100人抢。
1. 有10人成功进入“抢红包请求队列”,获得拆红包Token,其余90人直接被告知红包被抢完。
2. 这10人拆红包时,其中5人抢到,剩下5人被告知红包被抢完。
若不过滤请求,总的请求数:201次(生成红包1次、抢/拆红包各100次)
过滤请求后,总的请求数:111次(生成红包1次、抢红包100次、打开红包10次),并且其中90次请求会很快返回。