一. 问题思考
- 如何防止链接暴露?
- 怎么防止恶意请求?
- 高并发可能带来的问题?
- 限流?降级?熔断?
- 如何防止超卖问题?
二. 问题解决与设计
-
链接设计
1.前端将按钮置灰,前端定时请求后端服务器,获取最新的北京时间,到时间点才能点击。
2.采用MD5加密算法对URL加密,将URL动态化,通过前端代码获取url后台校验才能通过。注:MD5加密算法原理
1、数据填充
对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。
填充方法:在消息后面进行填充,填充第一位为1,其余为0。
2、添加消息长度
在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。
在此步骤进行完毕后,最终消息长度就是512的整数倍。
3、数据处理
准备需要用到的数据:4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476; 4个函数:F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z)); H(X,Y,Z)=X ^ Y ^ Z; I(X,Y,Z)=Y ^ (X | (~Z));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。
-
高并发下的安全问题
1.Redis集群,主从同步、读写分离
2. Nginx进行负载均衡
Nginx官方版本限制IP的连接和并发分别有两个模块:
limit_req_zone: 用来限制单位时间内的请求数,即速率限制,采用的漏桶 算法。
limit_req_conn: 用来限制同一时间连接数,即并发限制。
3. 解决缓存击穿、缓存雪崩、缓存穿透
解决方法:https://blog.csdn.net/qq_44837912/article/details/104425408
4. 限流
前端限流:点击按钮两下后间隔5秒才可以继续点击;
后端限流:对于能够执行生成担当和支付等操作的用户,当产品售空时,后端return了一个false,前端直接秒杀结束,后端也关闭后续无效的请求。
Tip:真正的限流还会有限流组件的加入例如:阿里的Sentinel、Hystrix等。 -
库存预热
提前把商品的库存从MySQL中加载到Redis,当秒杀结束,再异步的去修改库存。
-
Lua脚本是类似Redis事务,有一定的原子性,不会被其他命令插队,可以完成一些Redis事务性的操作。
写一个脚本把判断库存扣减库存的操作都写在一个脚本丢给Redis去做,那到0了后面的都Return False了是吧,一个失败了你修改一个开关,直接挡住所有的请求,然后再做后面的事情嘛。 -
限流&降级&熔断&隔离
- 功能简单实现:
技术难点1:库存和用户购买记录的同步
每次请求时,都先去判断用户购买记录是否属于二次购买,如果是二次购买则抛出异常,即购买失败,否则去判断库存是否为0,若库存为0则购买失败,若库存不为0,则进行减库存操作,紧接着记录用户的购买记录,为了保证减库存和用户购买记录增加同步,故在同一个事务中进行减库存和用户购买记录增加的操作。
技术难点2:前端不断向后端发送获取时间、库存的请求。
考虑到频繁访问数据库可能会将数据库刷爆,故得使用一个缓存去存储时间、库存等信息,因为Redis的QPS能达到10W+,故使用Redis去扮演这样的一个缓存角色,事先将商品信息、时间、库存缓存到Redis中,即使前端不断发送请求也无需担心数据库被刷爆。
提升1:
由于每次对库存和用户购买记录的操作都需要在数据库中进行,在高并发情况下可能对秒杀有一定影响,故考虑到使用消息队列进行消峰处理,因为Redis是一个单线程的原子化操作,故减库存的操作在Redis中进行,而用户购买记录在消息队列中操作,等秒杀结束在异步地将数据同步到数据库中。