Redis實戰(9)-SortedSet之再談遊戲充值排行榜(處理歷史充值記錄)

摘要

每當我們談起緩存中間件Redis的應用場景時,我們一般都會根據其數據結構聯想到對應的應用場景,有序集合SortedSet也不例外,"排行榜"一直都是與其緊密掛鉤、不得不談的其中一種實戰場景!本文我們將繼續再談"遊戲充值排行榜",介紹如何去處理歷史已經存在的充值記錄 或者 在將充值記錄塞入緩存Cache失敗時如何開啟後續的補償處理措施!

內容

在上篇文章中,我們已經給各位小夥伴介紹瞭如何基於Spring Boot2.0 + 緩存Redis的SortedSet以實際的代碼實戰一種典型的業務場景"遊戲充值排行榜",在文中我們介紹了這一業務場景兩大典型的核心功能模塊,即"用戶充值"、"獲取充值排行榜",各位小夥伴可以自行前往回顧!

然而,這世間本就沒有十全十美之物,"遊戲充值排行榜"這一業務場景也不例外,雖然我們基本上已經實現了該業務場景幾乎所有的功能模塊,但是我們卻忽略了其他兩種情況:

A.如果"充值排行榜"這一功能模塊是增量式的需求,那麼上線時如何去處理歷史的用戶充值記錄呢?你總不能說我們的"充值排行榜"對於以往充值的用戶記錄不生效吧?(那樣豈不令人笑掉大牙!)

B.雖然我們的代碼看似完美,但是要知道Bug是無處不在的,這些Bug有的是能一眼被洞穿的,也有的是後知後覺的,"用戶充值的過程"便是如此,如果用戶充值後插入數據庫DB成功、但是插入緩存Cache失敗(DB事務不回滾的前提),那毫無疑問,最終得出來的"充值排行榜"一定是不準確的(因為我們是直接從緩存Redis中獲取的)!

帶著這兩大問題,我們給大家提供了一種並非十全十美的,但是卻能保證"最終一致性"的充值排行榜的解決方案,那就是萬能的定時任務調度

既然是定時任務調度,那麼這個定時任務是做啥的呢?沒錯,它要完成的任務就是開啟一個定時時鐘,基於數據庫DB中的"用戶充值記錄表",藉助數據庫提供的Order By、Group By等查詢得出目前為止所有有效用戶的"充值排行榜",下面我們以實際的代碼進行實戰。


(1)直接建立一個定時任務調度類PhoneFareScheduler,並開發相應的方法實現具體的定時任務邏輯,其完整源代碼如下所示:

<code>/**補償機制:手機號碼充值排行榜
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260**/
@Component
public class PhoneFareScheduler {
private static final Logger log= LoggerFactory.getLogger(PhoneFareScheduler.class);

@Autowired
private PhoneFareMapper phoneFareMapper;

@Autowired
private RedisTemplate redisTemplate;
//時間頻度設定為30min,當然啦,具體的設定要根據實際情況而定

@Scheduled(cron = "0 0/30 * * * ?")
public void sortFareScheduler(){
log.info("--補償性手機號碼充值排行榜-定時任務");

this.cacheSortResult();
}

@Async("threadPoolTaskExecutor")
private void cacheSortResult(){
try {
ZSetOperations<string> zSetOperations=redisTemplate.opsForZSet();

List<phonefare> list=phoneFareMapper.getAllSortFares();
if (list!=null && !list.isEmpty()){
redisTemplate.delete(Constant.RedisSortedSetKey2);

list.forEach(fare -> {
FareDto dto=new FareDto(fare.getPhone());
zSetOperations.add(Constant.RedisSortedSetKey2,dto,fare.getFare().doubleValue());
});
}
}catch (Exception e){
log.error("--補償性手機號碼充值排行榜-定時任務-發生異常:",e.fillInStackTrace());
}
}
}/<phonefare>/<string>/<code>

值得一提的是,在該定時任務調度中我們設定的時間頻率為 每30min進行執行一次任務,實現"充值排行榜"的大洗盤!也就是說,如果前端"排行榜"頁面數據出現差錯,那麼其恢復正確的等待時間是30min(因為我們的定時任務就是前往數據庫DB,查詢獲取得到排行榜,當然啦,其前提是保證DB中的數據是正確無誤的!)


(2)其中,phoneFareMapper.getAllSortFares() 的作用就是前往數據庫Mysql,通過Group By、Order By和SUM等查詢得到排行榜,其完整的動態SQL如下所示:

<code>
<select>
SELECT
phone,
SUM(fare) AS fare
FROM
phone_fare
GROUP BY
phone
ORDER BY
fare DESC
/<select>/<code>

除此之外,@Async("threadPoolTaskExecutor") 的作用便是採用"線程池-多線程的方式異步執行定時任務",故而我們需要作一個全局的Config,用於配置線程池-多線程的相關信息:

<code>/**線程池-多線程配置
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260**/
public class ThreadConfig {
@Bean("threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setKeepAliveSeconds(10);
executor.setQueueCapacity(8);

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}/<code>

至此,我們已經擼完了"遊戲充值排行榜"這一完整業務的"補償機制"功能代碼了,在測試之前,我們先"偷偷"在數據庫表phone_fare中新增幾條充值記錄,代表"以前存在的歷史充值記錄"或者"插入DB成功,但插入緩存失敗的充值記錄",如下圖所示:

Redis實戰(9)-SortedSet之再談遊戲充值排行榜(處理歷史充值記錄)


最後我們基於Postman測試一波吧,下面一張圖足以說明一切了:

Redis實戰(9)-SortedSet之再談遊戲充值排行榜(處理歷史充值記錄)

好了,本篇文章我們就介紹到這裡了,建議各位小夥伴一定要照著文章提供的樣例代碼擼一擼,只有擼過才能知道這玩意是咋用的,否則就成了"空談者"!

對Redis相關技術棧以及實際應用場景實戰感興趣的小夥伴可以前往Debug搭建的技術社區的課程中心進行學習觀看:http://www.fightjava.com/web/index/course/detail/12!

其他相關的技術,感興趣的小夥伴可以私信Debug!


分享到:


相關文章: