目前在一家在線教育公司工做。負責題庫方面的工做。公司有人工智能批改服務,題庫這邊作的口語,寫做題須要提交到這個批改服務。因爲批改服務的負載能力較差,且暫時沒時間實現隊列,須要我這邊進行流控,1秒提交一次(平均批改時間爲1秒)。 因爲用戶主要是作閱讀,聽力題目,因此這個速度仍是能夠接受的。
前端在查看機批結果的頁面進行輪詢,5秒一次,直到我這邊返回批改爲功或失敗狀態。因爲不能讓用戶等待時間過久,因此設定2分鐘若是尚未批改爲功,就返回失敗,讓用戶選擇是否從新批改。前端
1.批改服務批改失敗。
2.批改服務與題庫服務通訊出問題。
3.批改隊列等待時間加批改時間超過2分鐘。redis
最開始的方案就是選擇用redis的list來當作隊列。每次用戶提交批改的時候,就將數據存放到list中。定時任務定時在list中取數據提交到批改服務。併發
上線以後發現機器批改的失敗率約爲2%,效果不是很理想。但至少批改服務不會在崩潰了。人工智能
因爲批改服務在自測的時候,失敗的機率低於1%。並且經過監控發現批改服務在高峯時期並無過高的負載。所以懷疑是題庫服務這邊的問題。
經過將批改失敗的日誌收集起來,發現大部分失敗都是超時。而且發現超時的這些記錄,都是接連出現,好比連着失敗10個這樣。而且都是併發較大的時間點。3d
1.在併發較大的時刻,用戶的提交速度超過了1秒每次,所以隊列裏就會形成堆積。好比用戶1秒提交2次。這樣每一秒隊列裏就會堆積一份數據。持續2分鐘後,隊列裏取出來的數據就是2分鐘前提交的數據了,這樣若是前端在請求查看結果的時候,題庫這邊查看目前時間已經超過了2分鐘了,就會直接設爲失敗。
2.用戶的請求堆積在了隊列裏超時以後,就可以從新批改了,這樣隊列裏就又多了一份重複數據,形成了資源浪費。日誌
解決上述問題2有一個很笨的辦法就是在list中查找一下是否已經有這份數據了,若是有就不插入了。可是list的查找速度很慢。並且redis沒有提供list的查找命令。只能將list中的內容一個個的拿到,再進行比較。
說到這裏可能有人就能想到了,用set,set能夠自動去重啊。這樣就不會有重複數據了。可是這樣怎麼保證提交批改的順序呢?對,用SortSet。用用戶提交批改的時間戳做爲score,就能很方便的爲批改排序了。
如今再來看上面的問題1,問題1要完全解決,確定須要機批那邊提升速度,要麼提升單機的處理能力,要麼可以根據請求量動態調整機器數量。但如今批改服務這邊暫時沒有精力,那就只能靠題庫服務這邊了。
題庫服務並不能提升批改速度,那就只能修改排序規則了。雖然排隊問題應該保證公平。可是有好多人都是不會在這個頁面等待的,而是先去作別的題目,等待一段時間後,再回來看批改結果。
所以最終的策略是優先處理2分鐘內提交的,而且優先處理先提交的。沒有2分鐘內提交的,再處理以前的,優先處理先提交的。blog
能夠看到上述的最終策略實際上是將隊列實時的一分爲2。排序
上圖可能畫的有點亂。。。能夠看到將SortSet分爲了兩個(只是邏輯劃分,redis中仍是一個,查的時候分開查)。查詢的順序就是先查批改時間在(當前時間的2分鐘前,當前時間]這個區間的,若是有的話,就提交到批改服務。不然的話查詢批改時間在[-inf, 當前時間的2分鐘前]這個區間的。若是有,就提交到批改服務。隊列
事情到這裏尚未完,雖然解決了從SortSet裏取數據的問題,可是往SortSet裏放數據有一點問題。SortSet會自動去重,若是值同樣,可是score不同的話,SortSet也認爲是同一個。也就是說用戶第一次提交批改,2分鐘後還沒輪到他,此時他點擊從新提交,並不會更新他的提交時間。不能移動到SortSet的最前面。
所以在將數據添加到SortSet的時候,須要查詢下這份數據對應的分數,獲得上次提交的時間。若是返回null,表明這是set中沒有,放心的push,若是上次的時間超過了2分鐘,就更新score爲當前時間戳,不然不作處理。資源
最終經過使用SortSet實現隊列,使得失敗率降爲了百分之0.5左右。