记录可满足10W人同时在线 1W并发的红包雨活动实现过程

场景描述:

跨年红包雨活动,分为五场,2018-12-31 20:00以后每个整点一场,每场红包雨持续1分钟。

有以下几个问题需要思考

 

思考一:10W人同时使用系统保证服务正常

从某云购买负载均衡SLB 10台 按量付费 规格是标准型2  按使用流量计费

nginx服务器,前端页面一定要用静态页面实现

 

思考二:如何保证奖品发出数量<=总奖品数量。

把所有的奖品按照单个奖品的总数平均分配到10台服务器,每台服务器上的单个奖品在平均分为5场

把以上分配好的奖品生成记录插入数据库。如某优惠券有100个:平均每台服务器上分10个,平均每台服务器上的每场分2个奖品,生成100条记录插入数据库,每条记录有对应的服务器编号,场次编号,奖品id等信息。

后台语言java实现,每场奖品数据随机打乱放入队列中。每来一个请求,若中奖,则从队列中移除该奖品,同时把该用户的中奖信息保存到redis。涉及到线程安全问题,一定要做好。保证一个机器上的某一场的某个奖品不会被多个人同时中奖。

负载均衡一定要设置ip进入的规则“轮询”,把每个请求按顺序逐一分配到不同的server,保证每台服务器上的奖品都能被均匀领到。

 

思考三:程序的灵活性,如何控制每场开始时间、奖品数量、中奖机率等等。

机器编号如何生成?不应该打10个包,每个包配置一个服务器编号,假如有100个不是要疯了?

可以用redis的自增方法,每启动一台服务获取key的值后保存到内存中,并且对该key的value+1操作。

提供接口,可以随时更改中奖机率和设置开始时间等等,每场间隔一个小时,有足够时间执行下一场的操作。

......

抽奖方法的核心代码:

public String init(@PathVariable(value = "openId") String openId) {
        // 本地缓存
        Repository repo = Repository.instance;

        // 一轮结束
        if (repo.endFlag > 0) {
            return ResultUtil.error(201, blessArr[(int) (Math.random() * blessArr.length)]);
        }
        // 查看当前奖励是否开始
        if (System.currentTimeMillis() < repo.prizeStart) {
            return ResultUtil.error(201, blessArr[(int) (Math.random() * blessArr.length)]);
        }

        // 直接比较,不用任何变量,如果数值超过128不能用==比较
        if (repo.prizeNum != (int) (Math.random() * repo.prizeRate)) {
            // 不要使用Exception的机制,内存和时间消耗很大
            return ResultUtil.error(201, blessArr[(int) (Math.random() * blessArr.length)]);
        }

        // 将中奖信息保存到缓存(setNX排他) 奖品批次:openId : 奖品内容(需要序列化,这里用toString代替)
        PeAcHbyPrize prize;
        if (redisUtil.incr("flag:" + repo.prizeGroup + openId, 1) <= repo.prizeCount) {
            // 取得奖品
            prize = repo.pollPrize();
            if (null == prize) {
                // 在本服务器上没奖,可能在别的服务器还有,要回退中奖次数
                redisUtil.decr("flag:" + repo.prizeGroup + openId, 1);
                return ResultUtil.error(201, blessArr[(int) (Math.random() * blessArr.length)]);
            }
            redisUtil.lSet("prize:" + repo.prizeGroup + openId, JSONObject.toJSONString(prize));
        } else {
            return ResultUtil.error(201, blessArr[(int) (Math.random() * blessArr.length)]);
        }
        log.info("{}--{}", openId, prize.getId());
        return ResultUtil.success(prize);// 成功
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章