生产环境亿级用户并发发券记录

本文介绍了一种在产品积分转移场景下,通过批量抽奖功能进行积分消耗的方法,并详细讨论了防并发策略,包括使用Redis进行并发控制和开关切换,以及单线程与多线程抽奖的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:产品需要,需消耗原有卡积分,转移到新卡。进行积分抽奖活动,其中用户可使用批量抽奖功能(10次)【单次请求第三方接口发送积分】。

分析:1 方法需进行防并发校验。【已有自定义注解,采用userId为key,redis进行防并发处理 ttl 获取-1 key没有设置失效时间; -2 key不存在; 正数 key的失效时间 】

           2 采用单线程循环及多线程两种方法进行积分发放,redis中做开关切换

           3 用户当日抽奖次数记redis,incry

关键代码实现:
public static final ExecutorService cachedThreadPool = Executors.newFixedThreadPool(50);

private Result<NewExpressResultVO> doReceivePacket(NewExpressCardReqDTO req,int times,NewExpressInfoBO acInfoCacheBO){
    NewExpressResultVO newExpressResultVO = new NewExpressResultVO();
    //成功赠送总金额
    AtomicInteger totalAmt = new AtomicInteger(0);
    //成功赠送次数
    AtomicInteger totalTimes = new AtomicInteger(0);
    CountDownLatch countDownLatch = new CountDownLatch(times);
    if(acInfoCacheBO.getMultiThreadSwitch().equals("1")){
        for(int i=0;i<times;i++){
            cachedThreadPool.submit(()-> {
                try {
                    sendPacket(totalTimes,totalAmt,req,times,newExpressResultVO,acInfoCacheBO.getMultiThreadSwitch());
                }
                catch (Exception e) {
                    logger.error("赠送金多线程抽奖失败 doReceivePacket 入参req{}",JSONObject.toJSONString(req));
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            logger.error("赠送金多线程抽奖失败 doReceivePacket 入参req{}",JSONObject.toJSONString(req));
        }
    }else {
        for(int i=0;i<times;i++){
            sendPacket(totalTimes,totalAmt,req,times,newExpressResultVO,acInfoCacheBO.getMultiThreadSwitch());
        }
    }
    newExpressResultVO.setTotalAmt(totalAmt.intValue());
    newExpressResultVO.setTotalTimes(totalTimes.intValue());
    return ResultUtil.success(newExpressResultVO);
}

//调用微服务发券
private void sendPacket(AtomicInteger totalTimes,AtomicInteger totalAmt,NewExpressCardReqDTO req,int times, NewExpressResultVO newExpressResultVO,String multiThreadSwitch){
    Map<String, Object> sentPacketMap = sentCempPacketParams(req);
    RestRespDTO<CommonPacketExchangeResult> result=null;
    if(multiThreadSwitch.equals("1")) {
         result = commonAcManager.receiveCommonActivityPacketList(sentPacketMap);
    }else {
         result = commonAcManager.receiveCommonActivityPacket(sentPacketMap);
    }
    if(result!=null && result.getSuccess()){
        totalTimes.addAndGet(1);
        totalAmt.addAndGet(result.getObj().getCommonAwardPacketRecordsResult().getCommodityCount());
        DefaultRedisCache.hincrBy(TIMES_KEY,req.getUserId(),1);
        if(DefaultRedisCache.ttl(TIMES_KEY)==-1) {
            DefaultRedisCache.expire(TIMES_KEY, DateUtils.getRedisDate());
        }
        if(times==1){
            newExpressResultVO.setPacketId(result.getObj().getCommonAwardPacketRecordsResult().getPacketId());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值