天天看點

交易系統高并發下的幂等性設計原則一、介紹二、用戶端設計原則三、服務端設計原則

一、介紹

幂等性就是針對同一個請求,不管該請求被送出了多少次,該請求都将被視為同一個請求,服務端不應該将同一個請求進行多次處理,以确認處理邏輯的正确性,針對交易性系統幂等性的設計尤為重要,否則由于網絡或伺服器處理逾時等問題,就會造成交易混亂,最嚴重的後果就是亂扣使用者的錢,造成投訴満天飛。

二、用戶端設計原則

系統設計時,一定是要從最壞的角度上去考慮,如網絡問題、伺服器問題,甚至于包括人為的攻擊行為,如果純粹從服務端的角度去做實作保證,一個是系統的複雜度會加大,二個也會對系統産生更大的并發壓力,因而用戶端的合理設計也非常重要,舉一個示例:

使用者在網頁端或APP端(統稱為用戶端)上購買一個商品,通常都是以觸發按鈕的行為送出該請求,如果用戶端沒有做請求送出控制,那麼由于網絡原因或者系統繁忙等原因(這是非常常見的場景),沒有在使用者期望的時間之内響應,那麼使用者就會再次點選,那麼這個請求又會發往服務端,那麼服務端又會再次對這個請求進行處理。

針對這種場景,服務端就會接收處理到多個訂單請求,進而産生多條不合理的使用者訂單。此時在用戶端稍微優化一下,當使用者的請求送出以後,将訂單送出的請求按鈕置為不可用的狀态,待請求成功響應後跳轉到下一步處理邏輯,或者是提單請求逾時後再将訂單送出按鈕置為可用,提示使用者上一次的送出可能已經失敗,并允許使用者再次送出。

當然用戶端這樣設計并不能完全做到幂等性原則,因為使用者同樣可以針對相同的訂單執行多次送出,服務端如果沒有做控制,還是會産生多個訂單,隻是讓錯誤變得優雅了一點。這個時候需增加令牌政策,在下單之間,針對目前訂單從服務端擷取一個Token,每次送出的時候都從把該Token帶上,針對同一個訂單帶上相同的值,這樣服務端就可以根據該Token來判斷是不是一個已經處理的了訂單。

說了那麼多,看文字太累,還是看圖友善,來一個:

交易系統高并發下的幂等性設計原則一、介紹二、用戶端設計原則三、服務端設計原則

三、服務端設計原則

根據不同的應用場景,服務端可以使用不同的解決方式,如:

1、資料庫的樂觀鎖;
2、防重表;
3、token令牌;
4、分布式鎖;
5、異步處理支付;
           

其中資料庫的樂觀鎖和防重表的使用,都是涉及到資料的參與,在高并發的應用場景中,業務的判斷邏輯盡量不要使用資料庫參與,特别是RDBMS的參與,因為RDBMS天生具有不易擴充及事務處理屬性,吞吐量上都會有相應的瓶頸,因而用資料庫做業務邏輯的控制不是我的菜,這裡就不會重點說這兩種方式。

1、Token令牌+分布式鎖的方式

Token是用于确定交易的唯一屬性,也是服務端用于檢驗目前交易是否合法交易的依據,但是在分布式的複雜環境中,如果沒有分布式鎖的控制,同一筆交易就可能會被處理多次,因而為了确認交易的幂等性,Token令牌和分布式鎖必須要一起使用。

實作邏輯步驟如下:

1、服務端根據交易前請求生成對應的Token,儲存于服務端的Token庫中,通常是緩存叢集中,并将生成好的Token庫下發給用戶端;
2、用戶端在每次請求的時候,都帶上對應的Token;
3、服務端擷取該Token對應的鎖,如果擷取成功,則繼續下面的步驟;
4、判斷是否該Token是否合法,如果合法則繼續下一步;
5、處理真實的業務邏輯;
6、業務處理成功後,從緩存中删除該Token;
7、删除擷取的分布式鎖;
           

實作邏輯參見下圖:

交易系統高并發下的幂等性設計原則一、介紹二、用戶端設計原則三、服務端設計原則

其中分布式鎖的擷取,可以通過Redis和Zookeeper實作,請參看筆者的另外兩篇分别介紹通過Redis和Zookeeper實作分布式的文章:

叢集環境中使用Redis實作分布式鎖兩種方式

叢集環境中使用Zookeeper實作分布式幂等控制

2、異步處理

異步處理,通常的做法是将認為需要消費的交易,送出到消息隊列中,并注冊監聽事件,待交易被處理完後,再由處理交易的應用回調注冊的監聽事件回報處理的結果。交易處理的排程應用,需要負責對交易的處理符合幂等性的原則,将重複請求的交易請求做去重處理。

實作邏輯見下圖:

交易系統高并發下的幂等性設計原則一、介紹二、用戶端設計原則三、服務端設計原則

從邏輯處理上可以看到,隻要交易處理器足夠多,處理速度也不一定會受到多少的影響,交易生産者和交易接收者甚至可以同步傳回結果,當交易接收者接收處理結果逾時後,再提示使用者過一會兒檢視交易的處理結果。

繼續閱讀