当前位置:网站首页>秒杀系统的设计与实现思路
秒杀系统的设计与实现思路
2022-07-05 22:52:00 【秃了也弱了。】
文章目录
写在前面
秒杀大家都不陌生,而且是电商项目必备的一个技能点。
但是真正的秒杀服务是非常复杂的,秒杀具有瞬间高并发的特点,所以解决瞬间高并发的问题,就可以解决秒杀的问题。
今天就将秒杀系统完整的实现分解开,一起研究一下吧。
(有问题还请指正)
秒杀系统注意事项
服务单一职责+独立部署
秒杀服务是有很大风险的,一不小心就会造成服务宕机或者一瞬间占用大量服务器资源,所以秒杀服务必须独立部署,而且秒杀服务只做秒杀功能。
秒杀链接加密
防止恶意攻击,防止有人模拟秒杀请求造成服务器更大的压力;
防止链接暴露,防止工作人员提前秒杀商品。
库存预热+快速扣减
秒杀系统读多写少,我们可以先将库存总数预热,存入redis中,使用信号量来控制秒杀请求的数量。
动静分离
使用nginx做好动静分离,保证静态资源直接能够请求到,避免占用后端资源。(现在基本都是前后端分离项目,此处可忽略)
恶意请求拦截
识别非法攻击的请求进行拦截,可以从网关层拦截,判断用户是否登录。
流量错峰
使用各种手段,将流量分担到更大宽度的时间点。比如验证码、加入购物车,多加几步操作。
限流&熔断&降级
前端限流+后端限流。
限制每秒钟只能点击一次;限制总量;
后端快速失败、降级运行、熔断机制防止雪崩。
队列削峰
秒杀成功的所有商品,放入消息队列中,然后消费端慢慢创建订单等等逻辑。
具体实现
限流熔断降级
使用sentinel进行限流
详解sentinel:分布式系统的流量防卫兵
队列削峰
使用rockerMQ或者rabbitMQ进行削峰。
库存预热
使用定时任务,提前将商品信息、商品随机码(防止恶意攻击)、商品库存等信息存入redis。
伪代码:
/** * 缓存秒杀活动所关联的商品信息 */
private void saveProductInfo(List<Product> products) {
products.stream().forEach(products-> {
//准备hash操作,绑定hash
BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(SECKILL_CHARE_PREFIX);
products.getRelationSkus().stream().forEach(seckillSkuVo -> {
//生成随机码
String token = UUID.randomUUID().toString().replace("-", "");
String redisKey = seckillSkuVo.getPromotionSessionId().toString() + "-" + seckillSkuVo.getSkuId().toString();
if (!operations.hasKey(redisKey)) {
// 防止重复添加
//缓存我们商品信息
SeckillSkuRedisTo redisTo = new SeckillSkuRedisTo();
Long skuId = seckillSkuVo.getSkuId();
//1、先查询商品的基本信息,调用远程服务
R info = productFeignService.getSkuInfo(skuId);
if (info.getCode() == 0) {
SkuInfoVo skuInfo = info.getData("skuInfo",new TypeReference<SkuInfoVo>(){
});
redisTo.setSkuInfo(skuInfo);
}
//2、sku的秒杀信息
BeanUtils.copyProperties(seckillSkuVo,redisTo);
//3、设置当前商品的秒杀时间信息
redisTo.setStartTime(session.getStartTime().getTime());
redisTo.setEndTime(session.getEndTime().getTime());
//4、设置商品的随机码(防止恶意攻击)
redisTo.setRandomCode(token);
//序列化json格式存入Redis中
String seckillValue = JSON.toJSONString(redisTo);
operations.put(seckillSkuVo.getPromotionSessionId().toString() + "-" + seckillSkuVo.getSkuId().toString(),seckillValue);
//如果当前这个场次的商品库存信息已经上架就不需要上架
//5、使用库存作为分布式Redisson信号量(限流)
// 使用库存作为分布式信号量
RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + token);
// 商品可以秒杀的数量作为信号量
semaphore.trySetPermits(seckillSkuVo.getSeckillCount());
}
});
});
}
秒杀实现
/** * 商品进行秒杀(秒杀开始) * @param killId * @param key * @param num * @return */
@GetMapping(value = "/kill")
public String seckill(@RequestParam("killId") String killId,
@RequestParam("key") String key,
@RequestParam("num") Integer num,
Model model) {
String orderSn = null;
try {
//1、判断是否登录
orderSn = seckillService.kill(killId,key,num);
model.addAttribute("orderSn",orderSn);
} catch (Exception e) {
e.printStackTrace();
}
return "success";
}
/** * 当前商品进行秒杀(秒杀开始) * @param killId * @param key * @param num * @return */
@Override
public String kill(String killId, String key, Integer num) throws InterruptedException {
long s1 = System.currentTimeMillis();
//获取当前用户的信息
MemberResponseVo user = LoginUserInterceptor.loginUser.get();
//1、获取当前秒杀商品的详细信息从Redis中获取
BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(SECKILL_CHARE_PREFIX);
String skuInfoValue = hashOps.get(killId);
if (StringUtils.isEmpty(skuInfoValue)) {
return null;
}
//(合法性效验)
SeckillSkuRedisTo redisTo = JSON.parseObject(skuInfoValue, SeckillSkuRedisTo.class);
Long startTime = redisTo.getStartTime();
Long endTime = redisTo.getEndTime();
long currentTime = System.currentTimeMillis();
//判断当前这个秒杀请求是否在活动时间区间内(效验时间的合法性)
if (currentTime >= startTime && currentTime <= endTime) {
//2、效验随机码和商品id
String randomCode = redisTo.getRandomCode();
String skuId = redisTo.getPromotionSessionId() + "-" +redisTo.getSkuId();
if (randomCode.equals(key) && killId.equals(skuId)) {
//3、验证购物数量是否合理和库存量是否充足
Integer seckillLimit = redisTo.getSeckillLimit();
//获取信号量
String seckillCount = redisTemplate.opsForValue().get(SKU_STOCK_SEMAPHORE + randomCode);
Integer count = Integer.valueOf(seckillCount);
//判断信号量是否大于0,并且买的数量不能超过库存
if (count > 0 && num <= seckillLimit && count > num ) {
//4、验证这个人是否已经买过了(幂等性处理),如果秒杀成功,就去占位。userId-sessionId-skuId
//SETNX 原子性处理
String redisKey = user.getId() + "-" + skuId;
//设置自动过期(活动结束时间-当前时间)
Long ttl = endTime - currentTime;
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
if (aBoolean) {
//占位成功说明从来没有买过,分布式锁(获取信号量-1)
RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + randomCode);
//TODO 秒杀成功,快速下单
boolean semaphoreCount = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
//保证Redis中还有商品库存
if (semaphoreCount) {
//创建订单号和订单信息发送给MQ
// 秒杀成功 快速下单 发送消息到 MQ 整个操作时间在 10ms 左右
String timeId = IdWorker.getTimeId();
SeckillOrderTo orderTo = new SeckillOrderTo();
orderTo.setOrderSn(timeId);
orderTo.setMemberId(user.getId());
orderTo.setNum(num);
orderTo.setPromotionSessionId(redisTo.getPromotionSessionId());
orderTo.setSkuId(redisTo.getSkuId());
orderTo.setSeckillPrice(redisTo.getSeckillPrice());
rabbitTemplate.convertAndSend("order-event-exchange","order.seckill.order",orderTo);
long s2 = System.currentTimeMillis();
log.info("耗时..." + (s2 - s1));
return timeId;
}
}
}
}
}
long s3 = System.currentTimeMillis();
log.info("耗时..." + (s3 - s1));
return null;
}
redisson信号量
边栏推荐
- Expectation, variance and covariance
- 透彻理解JVM类加载子系统
- 利用LNMP实现wordpress站点搭建
- 实现反向代理客户端IP透传
- Finally understand what dynamic planning is
- The method and principle of viewing the last modification time of the web page
- Tiktok__ ac_ signature
- VOT Toolkit环境配置与使用
- Registration of Electrical Engineering (elementary) examination in 2022 and the latest analysis of Electrical Engineering (elementary)
- Media query: importing resources
猜你喜欢

并查集实践

d3dx9_ How to repair 31.dll_ d3dx9_ 31. Solution to missing DLL
![[speech processing] speech signal denoising based on Matlab GUI Hanning window fir notch filter [including Matlab source code 1711]](/img/03/8fa104b177698a15b7ffa70d4fb524.jpg)
[speech processing] speech signal denoising based on Matlab GUI Hanning window fir notch filter [including Matlab source code 1711]

透彻理解JVM类加载子系统

一文搞定class的微观结构和指令

d3dx9_ What if 29.dll is missing? System missing d3dx9_ Solution of 29.dll file

Negative sampling

PLC编程基础之数据类型、变量声明、全局变量和I/O映射(CODESYS篇 )

Google Maps case

The difference between MVVM and MVC
随机推荐
Vcomp110.dll download -vcomp110 What if DLL is lost
Three. JS VR house viewing
[speech processing] speech signal denoising and denoising based on MATLAB low-pass filter [including Matlab source code 1709]
Tiktok__ ac_ signature
Element positioning of Web Automation
Codeforces Global Round 19
Editor extensions in unity
派对的最大快乐值
Metasploit (MSF) uses MS17_ 010 (eternal blue) encoding:: undefined conversionerror problem
[speech processing] speech signal denoising and denoising based on Matlab GUI low-pass filter [including Matlab source code 1708]
Three.js-01 入门
如何快速理解复杂业务,系统思考问题?
Usage Summary of scriptable object in unity
LeetCode145. Post order traversal of binary tree (three methods of recursion and iteration)
Hainan Nuanshen tea recruits warmhearted people: recruitment of the product experience recommender of Nuanshen multi bubble honey orchid single cluster
Leetcode weekly The 280 game of the week is still difficult for the special game of the week's beauty team ~ simple simulation + hash parity count + sorting simulation traversal
Arduino measures AC current
d3dx9_ How to repair 31.dll_ d3dx9_ 31. Solution to missing DLL
fibonacci search
【Note17】PECI(Platform Environment Control Interface)