高并发(秒杀)关注的问题

高并发三宝:缓存、异步、队排好。 

秒杀商品上架

定时任务上架

/**
 * 秒杀商品定时上架
 *      每天晚上3点上架最近三天需要秒杀的商品
 */
@Slf4j
@Service
public class SeckillSkuScheduled {

    @Autowired
    SeckillService seckillService;

    @Autowired
    RedissonClient redissonClient;

    /**
     * 保证幂等性,分布式锁
     */
    @Scheduled(cron = "0 0 3 * * ?")
    public void uploadSeckillSku() {
        RLock lock = redissonClient.getLock(SeckillConstant.SECKILL_UPLOAD_LOCK);
        lock.lock(10, TimeUnit.SECONDS);
        try {
            seckillService.uploadSeckillSku();
            log.info("商品上架成功......");
        } finally {
            lock.unlock();
        }
    }

}
public void uploadSeckillSku() {
    R r = couponFeignService.getSeckillSessions();
    if ((int) r.get("code") == 0) {
        // 远程获取所有需要上架的秒杀活动
        List<SeckillSessionVO> sessions = r.getData("sessions", new TypeReference<List<SeckillSessionVO>>() {});
        // 缓存redis
        // 1、缓存活动信息
        cacheSessionInfo(sessions);
        // 2、缓存活动关联的商品信息
        cacheSessionSkuInfo(sessions);
    }
}

private void cacheSessionInfo(List<SeckillSessionVO> sessions) {
    sessions.stream().forEach(session -> {
        long startTime = session.getStartTime().getTime();
        long endTime = session.getEndTime().getTime();
        String key = SeckillConstant.SESSION_CACHE_PREFIX + startTime + "_" + endTime;
        if (!stringRedisTemplate.hasKey(key)) {
            List<SeckillSkuRelationVO> relationSkus = session.getRelationSkus();
            List<String> skuIds = relationSkus.stream().map(relationSku -> {
                return relationSku.getPromotionSessionId()+"_"+relationSku.getSkuId();
            }).collect(Collectors.toList());
            stringRedisTemplate.opsForList().leftPushAll(key, skuIds);
        }
    });
}

private void cacheSessionSkuInfo(List<SeckillSessionVO> sessions) {
    sessions.stream().forEach(session -> {
        BoundHashOperations<String, Object, Object> ops = stringRedisTemplate.boundHashOps(SeckillConstant.SECKILLSKU_CACHE_PREFIX);
        List<SeckillSkuRelationVO> relationSkus = session.getRelationSkus();
        relationSkus.stream().forEach(relationSku -> {
            // 判断信号量的key是否存在            
            if (!ops.hasKey(relationSku.getPromotionSessionId()+"_"+relationSku.getSkuId())) {
                SeckillSkuVO seckillSku = new SeckillSkuVO();
                // 商品秒杀信息
                seckillSku.setSkuSeckillInfo(relationSku);
                // 商品详细详细信息
                R r = productFeignService.getSkuInfo(relationSku.getSkuId());
                if ((int) r.get("code") == 0) {
                    SkuInfoVO skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVO>() {});
                    seckillSku.setSkuInfo(skuInfo);
                }
                // 商品秒杀时间
                seckillSku.setStartTime(session.getStartTime().getTime());
                seckillSku.setEndTime(session.getEndTime().getTime());
                // 秒杀token
                String token  = UUID.randomUUID().toString().replace("-", "");
                seckillSku.setToken(token);
                // 引入分布式信号量
                RSemaphore semaphore = redissonClient.getSemaphore(SeckillConstant.SKU_STOCK_SEMAPHONE + token);
                semaphore.trySetPermits(relationSku.getSeckillCount().intValue());

                ops.put(relationSku.getPromotionSessionId()+"_"+relationSku.getSkuId(), JSON.toJSONString(seckillSku));
            }
        });
    });
}

秒杀流程

处理秒杀请求

@GetMapping("/seckill")
public R seckill(@RequestParam("killId") String killId,
                 @RequestParam("randomCode") String randomCode,
                 @RequestParam("num") String num) {
    String orderSn = seckillService.seckill(killId, randomCode, Integer.parseInt(num));

    return R.ok().put("orderSn", orderSn);
}
@Override
public String seckill(String killId, String randomCode, Integer num) {
    // 获取当前登录用户
    MemberTO member = LoginInterceptor.threadLocal.get();
    BoundHashOperations<String, String, String> ops = stringRedisTemplate.boundHashOps(SeckillConstant.SECKILLSKU_CACHE_PREFIX);
    String json = ops.get(killId);
    if (!StringUtils.isEmpty(json)) {
        SeckillSkuVO seckillSkuVO = JSON.parseObject(json, SeckillSkuVO.class);
        // 校验合法性
        long startTime = seckillSkuVO.getStartTime();
        long endTime = seckillSkuVO.getEndTime();
        long currentTime = new Date().getTime();
        // 校验时间合法性
        if (currentTime >= startTime && currentTime <= endTime) {
            // 校验随机码和商品
            String token = seckillSkuVO.getToken();
            String redisKillId = seckillSkuVO.getSkuSeckillInfo().getPromotionSessionId() + "_" + seckillSkuVO.getSkuSeckillInfo().getSkuId();
            if (token.equals(randomCode) && redisKillId.equals(killId) && num <= seckillSkuVO.getSkuSeckillInfo().getSeckillLimit().intValue()) {
                // 校验是否购买过(幂等性处理)
                String redisKey = member.getId() + "_" + redisKillId;
                long ttl = endTime - currentTime;
                Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
                if (flag) {
                    RSemaphore semaphore = redissonClient.getSemaphore(SeckillConstant.SKU_STOCK_SEMAPHONE + token);
                    try {
                        boolean b = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
                        // 创建订单号
                        String orderSn = IdWorker.getTimeId();
                        // TODO 秒杀成功,发送MQ消息

                        return orderSn;
                    } catch (InterruptedException e) {
                        log.error(e.toString());
                    }
                }
            }
        }
    }

    return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值