为了防止“程序员或者内部人员”作弊,这里的地址可以通过时间戳和 Hash 算法来生成,也就是说这个地址只有系统知道,到了快秒杀之前才由系统发放出去 。
有人说浏览器/客户端如果存放的都是静态页面,那么“控制开始下单”的按钮,以及发送“下单请求”的按钮,也是静态的吗?
答案是否定的,其实静态页面是方便客户端好缓存,下单的动作以及下单时间的控制还是在服务器端 。
只不过是通过 JS 文件的方式发送给客户端,在快要秒杀之前,会把这部分 JS 下载到客户端 。
因为,其业务逻辑很少,基本只包括时间,用户信息,商品信息等等 。所以,其对网络的要求不高 。
同时,在网络设计上,我们也会将 JS 和 HTML 同时缓存在 CDN 上面,让用户从离自己最近的 CDN 服务器上获取这些信息 。
为了避免秒杀程序参与秒杀,在客户端也会设计一些问答或者滑块的功能,减少此类机器人对服务器的压力 。
文章插图
秒杀系统前端设计简图
代理层设计
说完了秒杀系统的前端设计,请求自然地来到了代理层 。由于用户的请求量大,我们需要用负载均衡加上服务器集群,来面对如此空前的压力 。
文章插图
代理层三大功能简图
在这一层是可以做缓存,过滤和限流的:
- 缓存,以 Nginx 为例,它可以缓存用户的信息 。假设用户信息的修改没有那么频繁,即使有类似的修改也可以通过更新服务来刷新 。总比从服务器上获取效率要高得多 。
- 过滤,既然缓存了用户信息,这里就可以过滤掉一些不满足条件的用户 。注意,这里的用户信息的过滤和缓存只是一个例子 。
同时,还可以根据风控系统返回的信息,过滤一些疑似机器人或者恶意请求 。例如:从固定 IP 过来的,频率过高的请求 。最重要的就是在这一层,可以识别来自秒杀系统的请求 。
如果是带有秒杀系统的参数,就要把请求路由到秒杀系统的服务器集群 。这样才能和正常的业务系统分割开来 。
- 限流,每个服务器集群能够承受的压力都是有限的 。代理层可以根据服务器集群能够承受的最大压力,设置流量的阀值 。
此时就需要调整代理层的流量阀值,将能够处理的请求流量减少,保护后端的应用服务器 。
当服务器恢复以后,又可以将阀值调回原位 。可以通过 Nginx+Lua 合作完成,Lua 从服务注册中心读取服务健康状态,动态调整流量 。
应用层设计
“秒杀系统”秒杀的是什么?无非是商品 。对于系统来说就是商品的库存,购买的商品一旦超过了库存就不能再卖了 。
防止超卖
超过了库存还可以卖给用户,这就是“超卖”,也是系统设计需要避免的 。为了承受大流量的访问,我们用了水平扩展的服务,但是对于他们消费的资源“库存”来说,却只有一个 。
为了提高效率,会将这个库存信息放到缓存中 。以流行的 redis 为例,用它存放库存信息,由多个线程来访问就会出现资源争夺的情况 。也就是分布式程序争夺唯一资源,为了解决这个问题我们需要实现分布式锁 。
假设这里有多个应用响应用户的订单请求,他们同时会去访问 Redis 中存放的库存信息,每接受用户一次请求,都会从 Redis 的库存中减去 1 个商品库存量 。
当任何一个进程访问 Redis 中的库存资源时,其他进程是不能访问的,所以这里需要考虑锁的情况(乐观,悲观) 。
文章插图
Redis 缓存承载库存变量
如果锁长期没有释放,需要考虑锁的过期时间,需要设置两个超时时间:
- 资源本身的超时时间,一旦资源被使用一段时间还没有被释放,Redis 会自动释放掉该资源给其他服务使用 。
- 服务获取资源的超时时间,一旦一个服务获取资源一段时间后,不管该服务是否处理完这个资源,都需要释放该资源给其他服务使用 。
推荐阅读
- 世界艾滋病日:“携手抗艾,重在预防”让世界彻底无“艾”
- 并使用java实现 一文彻底看懂Base64编码解码原理
- 快速弄懂Java 11 中的NIO 2.0
- QQ 终于“大”更新,网友:微信彻底输了
- 挽回男人的心最佳时间 男朋友彻底失望了还能挽回吗
- 蟑螂彻底灭绝的办法 为什么蟑螂永远灭不掉
- 5分钟让你彻底了解手机各项参数 我不允许有人不会买手机
- 华为手机这样清理内存,更加有效更加彻底,再也不怕手机卡顿了
- 看完让你彻底搞懂Websocket原理,附通过netty完成Websocket
- 六大茶系绿茶 白茶 黄茶 青茶 红茶 黑茶 这下终于弄懂了