實戰併發-使用分散式快取和有限狀態機

程式設計一生發表於2019-03-13

簡介    

這裡的併發不是高併發,只是將正式環境的一小段流量同時打到我的自測環境。一個請求同時多次傳送,真正意義上併發處理同一個資料,主要需求是保證資料冪等性和正確性。

主要技術是用分散式快取做多次相同請求的冪等處理和用有限狀態機來解決MQ訊息的不保證有序。

 

場景

k8s叢集可以進行事件監聽,靜兒這次使用了一個美團內網線下的小叢集。把這個小叢集的對node節點和pod節點的監聽事件傳送到MQ,3臺伺服器在同時工作。也就是說一個事件會被重複收到三次。其中兩臺機器事件傳送基本上是同時的,剩下一臺是我自己的電腦。因為在家裡,連著vpn,連線公司內網大概有2.5ms的延時。

在MQ事件的接收端,美團內部監控系統CAT上看到資料如下:

 

問題

當一個請求被重複在併發和有延遲的情況下會被重複收到。k8s自身也會短時間傳送一些相同的請求。這些重複的請求在不考慮重複執行的副作用前提下,每次都同樣的方式執行,後端的壓力也會非常大。如果考慮重複執行的副作用,就是說重複的請求不冪等,資料不準確了,整個服務就非常糟糕了。

另外,不管是MQ還是k8s事件,接收處理事件的服務都不能保證先收到的事件是先產生的。多臺機器的情況下,就算是先產生的,也會因為不同機器的處理速度不一樣,導致後產生的事件先被執行。

 

解決

如果下面解決方法中的概念不是很清楚,可以先看5W,再回來看方法。

使用分散式快取解決請求去重的問題

首先考慮對請求處理的維度。比如靜兒舉例的場景中,對node節點的watch事件,可以用node作為快取的key,用node需要處理的關鍵欄位作為value。如果請求中的資訊與快取中的一致,則直接返回不處理。不一致則先更新快取為最新值,再進行處理。

注意,因為保證訊息不會被併發處理。剛才的快取值獲取get和重設put操作都是用分散式鎖進行了加鎖的。

使用有限狀態機解決亂序的問題

之所以有「亂序」這個定義,說明系統本身是訊息的順序是有要求的。順著這個思路來考慮,可以順理成章的得到解決方案:設定好原本的一個介紹順序。如果收到事件A則執行事件B。如果收到事件B則執行事件C。如果沒收到事件A先收到了事件B,則把MQ訊息打回去重發。在打回去的階段事件A收到處理完了,這時候再收到事件B就可以繼續執行了。

剛才說的這種執行順序的方法就是有限狀態機。有限狀態機在程式裡的實現主要有兩種。一種是通過switch case的方法。就是如果A則B的最容易理解的實現。通常被稱為“可執行程式碼”方式。

另外一種方式是:如果每種狀態要做的處理比較複雜。用switch case比較難看。就可以將處理方法抽象成一個類。將這些類的例項放到對映表裡。這種實現通常被稱為“被動資料”方式。

 

5W

Q:為什麼MQ訊息不保證有序?

A:因為MQ訊息在伺服器上是分割槽儲存的,每個分割槽自己是有序的。分割槽被接收端消費的時候。一般也是多個接收端一起消費。中間的每個環節都是隻能保證區域性有序。如果想全域性有序。就需要分割槽只有一個,並且接收端伺服器是單點,而且一次只處理一個請求。

Q:MQ的使用上還有什麼關鍵注意點?

A:一般情況下MQ除了不保證訊息有序還不保證訊息不重複。因為在「網路不可達」的情況下,MQ不能確認訊息接收方收到了訊息必然會重試。重試除了本文講的冪等處理外,還可以採用每個訊息有唯一的ID+去重表實現。

Q:什麼是分散式快取?

A:分散式快取有時候也叫「集中式快取」。是相對於「本地快取」而言的。因為在目前的多伺服器部署(分散式)時代。「本地快取」這種將資訊儲存於當前伺服器上,其他伺服器無法感知。這時候採用一個專門的快取伺服器就可以解決這個問題。這就是分散式快取了。

Q:什麼是有限狀態機?

A:有限狀態機也稱為FSM(Finite State Machine),是表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型。FSM可以把模型的多狀態、多狀態建的轉換條件解耦。可以使維護變得容易,程式碼也更加具有可讀性。

 

總結

併發問題對系統的影響

系統壓力造成的可用性問題、多個請求同時對同一個資料產生作用產生的資料準確性問題。

解決方案

可用性問題可以從設計上在業務邏輯層之前對資料去重,例如可以使用分散式鎖,讓真正耗時的業務邏輯對相同的多個請求只執行一次。

資料準確性問題可以通過有限狀態機保證不論收到請求順序如何,都按照正確的邏輯來執行。

相關閱讀     

《程式常用的設計技巧》

《到底多大才算高併發?》

《美團分散式服務通訊框架及服務治理系統OCTO》

《學會用資料說話-分散式鎖究竟可以多少併發?》

《大話高可用》

《業務開發轉基礎開發,這三種「高可用」架構你會麼?》

相關文章