1. 需求簡介
根據用戶的測試情況,給他推薦相應的課程,然后根據學習時長可以獲得勛章
2. 效果圖
3. 功能拆解
3.1. 測試題
【要點】
1、 需要一個題庫,配好題目和答案選項;
2、需要根據用戶的答題情況(分數)抽取一定量的各種類型的題目
3、本次抽取的題目與上一次的題目重復率不得超過50%
4、提交答案
【難點】
這個功能最復雜的地方在於重復率不得超過50%,可以用兩次的題目去交集來判斷,也可以排除上一次的題目后再隨機,沒有用什么高深的算法(主要是不懂)這里我用的后者,不再贅述。
3.2. 生成計划
【要點】
1、從大數據那里獲取推薦內容
2、上一個計划未完成不允許開啟下一個計划
3、同種勛章只能獲得一次
4、保存數據(計划、計划詳情、勛章、消息)
【難點】
1、調大數據的接口時要考慮到失敗的情況,即使大數據的服務掛了,咱們也不能掛,為此加上try...catch,超時時間等等都是必要,這還不夠。萬一調大數據失敗了,返回保底數據。
2、考慮到重復提交的情況,需加分布式鎖
3.3. 計划首頁
【要點】
1、內容展示,包括分已購未購、免費付費、會員非會員、標簽等等
2、學習時長展示,包括時長、天數、當前階段、勛章等等
【難點】
1、查數據庫想都不要想,首選直接查Redis,但是Redis查的次數多了綜合起來也有可能慢,必要時可考慮本地緩存(內存緩存)
2、時長、天數等均從在Redis總累計,可直接獲取
3.4. 學習時長
【要點】
1、實時記錄
【難點】
1、線上類似的接口高峰時段可達到1分鍾84萬次請求,因此想都不用想,必須異步,我們采用MQ
2、盡管消費者是一條一條的消費,然而這並不意味着一秒鍾只消費一條,據觀察,線上其它類似MQ消費者平均一秒鍾消費300條
3、時長最終是要更新到MySQL數據庫中的,但是如此頻繁的寫數據,風險比較大,想來想去,最終選擇用定時任務在夜深人靜的時候從Redis同步到MySQL
4、學習時長是一個特別重要的參數,關系到挑戰任務,關系到勛章,因此以秒為單位,累計至1分鍾再寫入
5、在壓測的時候,我發現基於Redis的分布式鎖似乎並不能保證完全100%鎖住,雖然概率極低,但我好像碰到了
3.5. 樓層
【要點】
1、根據用戶計划的狀態展示不同的按鈕及背景圖
2、連續關閉兩次樓層后不再出現,新開啟計划后再次出現
【難點】
1、用戶關閉樓層的行為直接記到Redis中,不必存數據庫
3.6. 彈窗
【要點】
1、每個挑戰任務對應一個勛章,再加上最后一個獎勵勛章。任務是有順序的,因此彈窗也是,為此需要知道任務前后的關系
2、每個彈窗只彈一次,因此需要記錄用戶是否查看過
【難點】
1、用雙向鏈表將挑戰任務串聯起來,這樣就可以根據當前所處階段快速找到上一個或下一個任務
2、在Redis緩存結構上,我犯了一個錯誤。原因我想的是既然是有順序的要不就用List類型,但是后來發現更新的時候極其不方便,要把所有的元素先刪除(LTRIM命令),然后修改后重新插入,在一次壓測過程中發現里面大量重復數據,還沒刪完就已經插入了,於是越插入越多,這就是偷懶的代價,最終緊急改成Hash類型,這樣即使有重復也會被覆蓋。
3.7. 通知消息
【要點】
1、APP消息和站內信都要發
2、不用的階段的消息文案不一樣
【難點】
1、定時任務掃描
2、異步發送,保證互不影響
4. 壓測
所有的Redis查詢都是批量查詢,即使是這樣,響應時間依然很長,平均響應時間400ms左右。用PinPoint分析排查:
后來,加上了本地緩存Caffeine,感覺快了很多
順便看下其它指標