当前位置: 首页 > news >正文

【抽奖项目】|第二篇

前言:

        高并发的活动预热肯定不可以在数据库操作,需要redis,特别是这种秒杀活动更是需要注意,所以可以在高并发的前夕先进行活动预热。

 思路:

       1、 通过定时任务调度每分钟查询数据库也没有需要预热的活动

        2、采用分布式锁防止任务重复调度

        3、查询到预热活动需要信息全部进行redis存储

        4、生成令牌桶

                  细节:生成总奖品个数个令牌

                             每个令牌生成开始到结束时间的一个随机数

                              乘上1000,在额外加上一个三位数的随机数  ------防止奖品过多令牌重复

                              把令牌放入令牌桶

                              设置令牌和奖品的关系

        5、先按照时间大小排序,在压入redis

        6、改变预热状态

    @Scheduled(cron = "0 * * * * ?")
    public void execute() {
        //TODO 缓存预热

        // 获取当前时间的Calendar实例
        Calendar calendar = Calendar.getInstance();
        // 清除毫秒部分
        calendar.set(Calendar.MILLISECOND, 0);
        // 获取不带毫秒的Date对象
        Date now = calendar.getTime();
        //分布式锁,防止重复启动任务
        if (!redisUtil.setNx("game_task_"+now.getTime(),1,60L)){
            log.info("task started by another server!");
            return;
        }
        //查询将来1分钟内要开始的活动
        QueryWrapper<CardGame> gameQueryWrapper = new QueryWrapper<>();
        //开始时间大于当前时间
        gameQueryWrapper.gt("starttime",now);
        //小于等于(当前时间+1分钟)
        gameQueryWrapper.le("starttime",DateUtils.addMinutes(now,1));
        List<CardGame> list = gameService.list(gameQueryWrapper);
        if(list.size() == 0){
            //没有查到要开始的活动
            log.info("没有查到要开始的活动");
            return;
        }
        log.info("需要缓存预热的活动个数:{}",list.size());
        //有相关活动数据,则将活动数据预热,进redis
        list.forEach(game ->{
            //活动开始时间
            long start = game.getStarttime().getTime();
            //活动结束时间
            long end = game.getEndtime().getTime();
            //计算活动结束时间到现在还有多少秒,作为redis key过期时间
            long expire = (end - now.getTime())/1000;
//            long expire = -1; //永不过期
            //活动持续时间(ms)
            long duration = end - start;

            Map queryMap = new HashMap();
            queryMap.put("gameid",game.getId());


            //活动基本信息
            game.setStatus(1);
            redisUtil.set(RedisKeys.INFO+game.getId(),game,-1);
            log.info("活动ID:{},名称:{},开始:{},结束{}", game.getId(),game.getTitle(),game.getStarttime(),game.getEndtime());

            //活动奖品信息
            List<CardProductDto> products = gameLoadService.getByGameId(game.getId());
            Map<Integer,CardProduct> productMap = new HashMap<>(products.size());
            products.forEach(p -> {
                productMap.put(p.getId(),p);
            });

            //奖品数量等配置信息
            List<CardGameProduct> gameProducts = gameProductService.listByMap(queryMap);

            //令牌桶
            List<Long> tokenList = new ArrayList();
            gameProducts.forEach(cgp ->{
                //生成amount个start到end之间的随机时间戳做令牌
                for (int i = 0; i < cgp.getAmount(); i++) {
                    long rnd = start + new Random().nextInt((int)duration);
                    //为什么乘1000,再额外加一个随机数呢? - 防止时间段奖品多时重复
                    //记得取令牌判断时间时,除以1000,还原真正的时间戳
                    long token = rnd * 1000 + new Random().nextInt(999);
                    //将令牌放入令牌桶
                    tokenList.add(token);
                    //token到实际奖品之间建立映射关系
                    redisUtil.set(RedisKeys.TOKEN + game.getId() +"_"+token,productMap.get(cgp.getProductid()),expire);
                }
            });
            //排序后放入redis队列
            Collections.sort(tokenList);
            log.info("load tokens:{}",tokenList);

            //从右侧压入队列,从左到右,时间戳逐个增大
            redisUtil.rightPushAll(RedisKeys.TOKENS + game.getId(),tokenList);
            redisUtil.expire(RedisKeys.TOKENS + game.getId(),expire);

            //奖品策略配置信息
            List<CardGameRules> rules = gameRulesService.listByMap(queryMap);
            //遍历策略,存入redis hset
            rules.forEach(r -> {
                redisUtil.hset(RedisKeys.MAXGOAL +game.getId(),r.getUserlevel()+"",r.getGoalTimes());
                redisUtil.hset(RedisKeys.MAXENTER +game.getId(),r.getUserlevel()+"",r.getEnterTimes());
                redisUtil.hset(RedisKeys.RANDOMRATE +game.getId(),r.getUserlevel()+"",r.getRandomRate());
            });
            redisUtil.expire(RedisKeys.MAXGOAL +game.getId(),expire);
            redisUtil.expire(RedisKeys.MAXENTER +game.getId(),expire);
            redisUtil.expire(RedisKeys.RANDOMRATE +game.getId(),expire);


            //活动状态变更为已预热,禁止管理后台再随便变动
            game.setStatus(1);
            gameService.updateById(game);

        });
    }

                                

        

        

相关文章:

  • 环境配置 | 5分钟极简Git入门:从零上手版本控制
  • vb编程有哪些相关的IDE开发工具vb.net,Basic语言?
  • JVM常用概念之常量
  • zsh: command not found: adb 报错问题解决
  • mac 苍穹外卖 前端环境配置
  • 电机主备互投功能优化_多台设备均衡运行
  • 梯度下降法及其变体详解
  • 为什么会出现redis数据库?redis是什么?
  • 电力时间同步系统,京准电钟电子助力增效
  • Llama 3.1部署教程(非常详细)从零基础入门到精通,看完这一篇就够了
  • Netty基础—3.基础网络协议二
  • 游戏引擎学习第153天
  • 计算机网络(第二章)
  • 身处AI浪潮:博客价值的新思考与IT从业者的新征程
  • VSCode 配置优化
  • C语言算法实现教程:从基础到进阶
  • Javaweb后端全局异常处理器
  • 道路运输安全员考试:深度剖析与备考指南
  • Python 内存管理进阶:打造自定义内存池,释放性能潜力
  • 简单工厂 、工厂方法模式和抽象工厂模式
  • 印巴战火LIVE丨“快速接近战争状态”?印度袭击巴军事基地,巴启动反制军事行动
  • 泰特现代美术馆25年:那些瞬间,让艺术面向所有人
  • 欧盟决意与俄罗斯能源彻底决裂之际,美国谋划新生意:进口俄气对欧转售
  • 人民财评:网售“婴儿高跟鞋”?不能让畸形审美侵蚀孩子身心
  • 英国和美国就关税贸易协议条款达成一致
  • 国家主席习近平同普京总统签署关于进一步深化中俄新时代全面战略协作伙伴关系的联合声明