交易系統高並發下的冪等性設計原則


一、介紹

冪等性就是針對同一個請求,不管該請求被提交了多少次,該請求都將被視為同一個請求,服務端不應該將同一個請求進行多次處理,以確認處理邏輯的正確性,針對交易性系統冪等性的設計尤為重要,否則由於網絡或服務器處理超時等問題,就會造成交易混亂,最嚴重的后果就是亂扣用戶的錢,造成投訴満天飛。

 

二、客戶端設計原則

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

用戶在網頁端或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、異步處理

異步處理,通常的做法是將認為需要消費的交易,提交到消息隊列中,並注冊監聽事件,待交易被處理完后,再由處理交易的應用回調注冊的監聽事件反饋處理的結果。交易處理的調度應用,需要負責對交易的處理符合冪等性的原則,將重復請求的交易請求做去重處理。

實現邏輯見下圖:

從邏輯處理上可以看到,只要交易處理器足夠多,處理速度也不一定會受到多少的影響,交易生產者和交易接收者甚至可以同步返回結果,當交易接收者接收處理結果超時后,再提示用戶過一會兒查看交易的處理結果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM