本文主要介绍秒杀场景下的系统设计思路和方案。
秒杀活动和其他正常的业务活动相比,具有瞬时流量巨大的特征,所以需要和其他业务进行隔离,并且也需要更多的资源投入才能支撑这样大流量的业务,还需要考虑请求失败情况的处理方案。
![image-20230613004940173](https://i-blog.csdnimg.cn/blog_migrate/4ca4bab4c6b18bb153d7bada9ab75f90.png)
进行秒杀设计的重点问题和设计原则有:
- 需要进行隔离设计,包括业务隔离、系统隔离、数据隔离;
- 需要进行前置的负债均衡和限流处理;
- 提前对热点数据进行处理;
- 需要考虑失败兜底策略和数据一致性的问题;
一、业务说明
用户视角的业务流程:
运营视角的业务流程:
二、系统架构设计
下图从一次用户秒杀请求来分析会涉及到的部分。
由于秒杀请求流量巨大,须有一般会在服务前端前置Nginx服务器,一个作用是用来做负载均衡,一个作用是用来做反向代理,可以用来实现静态资源服务器。
下一个阶段就来到了web网关服务,这个阶段可以实现常见的认证和鉴权功能,同时也可以进行流量筛选,通过设置黑白名单,可以过滤一部分流量。
下一个节点就是秒杀的业务服务,通常秒杀业务不会是一个服务实例能支撑的,所以在设计的时候要考虑到后续的弹性扩缩容,需要无状态设计。
最后的业务一定是请求到自己业务的数据库或者请求外部服务,针对自己的内部服务,一般会涉及数据库+缓存的支撑或者写入消息中间件进行异步处理,也可能设计服务内的RPC调用,针对外部服务,可能会涉及到HTTP调用。
常见的架构设计:
三、数据隔离策略
由于秒杀业务和普通业务大有不同,为避免其瞬时流量影响到普通业务,所以需要将其进行隔离包括。一般情况下可以从业务、系统、数据层面进行考虑。
业务隔离
为和普通业务区分开,针对秒杀业务一般会有单独的入口也详情页面,对于运营人员来说,也会由单独的秒杀业务提报平台。通过不同的入口来隔离秒杀业务和普通业务。
系统隔离
秒杀业务自身服务需要单独部署,并根据业务流量具有弹性扩缩容的能力,根据实际情况决定是否需要对涉及到的服务也做单独拎出来支持秒杀业务。
数据隔离
主要针对的数据库服务,秒杀是一个多读多写的场景,如果不和其他业务进行数据库层面的隔离,那可能会影响到正常的业务。
四、不同阶段的处理方案
1.秒杀前的处理
预约
从业务角度考虑,在秒杀之前可以引入预约制度,只有预约后的用户才能进行秒杀,这样在进入秒杀前就能拦截一大半的流量,本身用来进行秒杀的商品不会太多,虽然说秒杀的业务价值是广告营销,但吸引一千倍还是一万倍的用户来秒杀其实区别不大。
热点数据预热
秒杀的商品一般固定,其商品详情信息一般也固定,针对秒杀时候的多读情况,可以先进行热点数据的预热,在开启秒杀活动前,将商品详情信息读入到redis中,这样在进行秒杀的时候不至于对数据库造成太大的压力。
2.秒杀中的处理
验证码和限购
在进入秒杀阶段了,流量会在瞬间增长,所以从业务角度来说,可以将这些流量分散开来,避免对服务产生较大的冲击,监控流量过大,可以加入验证码校验这种策略,将流量后移和平滑,可以选择Happy-Captcha
进行验证码校验,代码实现如下:
<dependency>
<groupId>com.ramostear</groupId>
<artifactId>Happy-Captcha</artifactId>
<version>1.0.1</version>
</dependency>
---------------------------
@RestController
@RequestMapping("/happy-captcha")
public class HappyCaptchaController {
@GetMapping("/generator")
// @TokenCheck(required = false)
public void generatorCode(HttpServletRequest request, HttpServletResponse response){
HappyCaptcha.require(request, response)
.style(CaptchaStyle.ANIM)//设置动画
.type(CaptchaType.ARITHMETIC_ZH)//设置算数验证码
.build().finish();
}
@GetMapping("/verify")
// @TokenCheck(required = false)
public String verify(@RequestParam String verifyCode, HttpServletRequest request){
Boolean aBoolean=HappyCaptcha.verification(request, verifyCode,true);
if(aBoolean){
HappyCaptcha.remove(request);
return "通过";
}
return "不通过";
}
}
除了可以在秒杀之前设置预约环境,还可以没每个用户、每个区域、每个时间点还有每个IP等设置限购,通过这正方式也可以减少秒杀时候用户重复请求。
消息队列异步处理
在技术层面,可以通过消息队列来削减秒杀时候海量请求对系统和数据库造成的压力,先请求到消息队列,通过消息队列的排队和限流机制,来减轻后端的压力,比如在用户下订单的操作上,可以先将订单处理的任务放到消息队列中,再通知下游的多线程来处理。
负载均衡和限流
处理大流量场景单服务实例肯定能是不行的,在秒杀这种情况需要多服务共同支撑,所以选择哪个服务实例处理就需要用到负载均衡,一般会使用负载均衡服务器Nginx在进入系统前进行分流,减轻单实例的压力。
针对于限流处理,这个Nginx也能够实现,这个只是在网关层面的实现,在后面的业务处理中也可以处理,比如通过线程池的排队和消息队列的排队处理等。
可靠性处理
在设计秒杀过程,需要考虑数据一致性的问题,需要进行幂等性设计,防范重复下单导致的数据不一致。
同时需要考虑分布式事务的问题,当出现下单失败的情况,由于订单数据、商品数据、支付数据存在不同的库中,需要同时回滚。
3、秒杀后的处理
主要涉及一些兜底处理方案,包括但流量过大进行的降级和熔断等。
针对秒杀场景,由于价格便宜,肯定会有很多黑产账户来抢购,所以怎么识别正常用户就需要进行风控系统处理。
4.常见问题
TODO
- 可以写的更详细,针对常见问题进行说明;