場景描述:
跨年紅包雨活動,分爲五場,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);// 成功
}