目錄
文章目錄
- 目錄
- 前言
- 正文
- 1 介面冪等性
- 1.1 案例
- 2 解決方案
- 2.1 token機制
- 2.2 去重表
- 2.3 redis 的 SETNX鍵值
- 2.4 狀態機冪
- 2.5 樂觀鎖(更新操作)
- 2.6 悲觀鎖(更新操作)
- 結語
前言
今天的主題:介面冪等性的解決方案,本來是想把物件的存盤程序和記憶體布局肝出來的,但是臨時產生了變化,哈哈,這部分內容我們留在下一期吧,有句話說的好,好事多磨,對吧,
在實際專案開發中介面是我們在開發中經常接觸到的,而且是經常經常要寫,每一個專案可能都會伴隨著大量的介面開發,在moon來涂鴉的這幾個月,基本上就是在與介面作斗爭了,新需求除了業務相關就是設計表和介面撰寫了,
當然,在介面設計中我們要考慮很多問題,安全性,格式,設計等等,今天我們先來聊聊,在高并發環境下,介面冪等性的解決方案有哪些,
正文
1 介面冪等性
就是說在多次相同的操作下保證最終的結果是一致的,
其實這個概念還是比較簡單的,很容易理解,那我們思考一個問題,如果不保證介面冪等性會有什么問題?
1.1 案例
我們簡單的舉個例子,現在有一個介面,提供了轉賬的功能,a要給b轉賬1000元,正常情況下我們介面一次性就呼叫成功了,但是卻因為網路抖動等其它原因沒有成功,于是就開始不停的重試,突然網路好了,但是這時卻連續發出去了三個請求,但是這個介面沒有保證冪等性,于是從結果上來看就是a給b轉了3000元,這顯然是程式業務邏輯上不能接受的(其實moon可以當b的),
2 解決方案
2.1 token機制
token機制其實是比較簡單的,我們先來簡單的說一下流程,
- 首先客戶端先請求服務端,服務端生成token,每次請求生成的都是一個新的token(這個token一定要設定超時時間),將token存入redis當中,然后將token回傳給客戶端,
- 客戶端攜帶剛付訓傳的token請求服務端做業務請求,
- 服務端收到請求,做判斷,
- 如果token在redis中,則直接洗掉該token,然后繼續做業務請求,
- 如果token不在redis中,代表已經執行過當前業務了,則不執行業務,
圖示如下:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Q1QNNZFG-1605604499314)(https://imgkr2.cn-bj.ufileos.com/7a91ce64-9c63-4d64-87dd-73a1a4dc7584.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=poJSuHUij9iq1FqiUWghjmc8r5Q%253D&Expires=1605668431)]](https://img.uj5u.com/2020/11/18/194310181050361.png)
token機制實作方式還是比較簡單的,但是其實對于我們某些回應速度要求很高的業務不太友好,缺點就是需要多一次請求獲取token的程序,
正常來說是每次請都會生成一個新的token,如果有極限情況下,有兩個請求都帶著相同的token進來,會存在都走入判斷是否存在的程序,可能都會同時查到存在,這樣也會有問題,針對這種情況,我們可以在洗掉前判斷下是否存在,存在就洗掉,為了保證原子性,這部分邏輯建議使用lua腳本完成,
2.2 去重表
去重表的機制是根據mysql唯一索引的特性來的,我們先來說下它的流程:
- 首先客戶端先請求服務端,服務端先將這次的請求資訊存入一張mysql的去重表中,這張表要根據這次請求的其中某個特殊欄位建立唯一索引,或者主鍵索引,
- 判斷是否插入成功
- 如果插入成功,則繼續做后續業務請求,
- 如果插入失敗,則代表已經執行過當前請求,
圖示如下:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-z2ECvxRG-1605604499316)(https://imgkr2.cn-bj.ufileos.com/d4796cc2-3c43-4b50-bc73-60496c5efb44.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=Q9PrFYKNh6WfFa3HBrvwQCZ0gTo%253D&Expires=1605682457)]](https://img.uj5u.com/2020/11/18/194310181050362.png)
去重表機制的問題有兩點:
- 1.mysql容錯性,也就是mysql本身如果不是高可用的那么業務可能會受到影響:
- 2.既然是唯一索引,自然在寫表的時候就沒有辦法用到changbuffer,每次都要從磁盤查出來判斷再寫入,對于一個高并發的介面來說,這些都是需要考慮的因素,
2.3 redis 的 SETNX鍵值
程序如下:
- 首先客戶端先請求服務端,服務端將能代表這次請求業務的唯一欄位以 SETNX 的方式存入redis,并設定超時時間,超時時間可以根據業務權衡,
- 判斷是否插入成功
- 如果插入成功,則繼續做后續業務請求,
- 如果插入失敗,則代表已經執行過當前請求,
這里我們是利用了redis setnx 的特性來完成的,
setnx:只在鍵key不存在的情況下,將鍵key的值設定為value,若鍵key已經存在,則SETNX命令不做任何動作,命令在設定成功時回傳1,設定失敗時回傳0,
圖示如下:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4veJGy5D-1605604499318)(https://imgkr2.cn-bj.ufileos.com/8f7ce2f5-bdb1-4412-8426-551ddc08fe1d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=IF2Y%252FdRvP7WeDMUJxxh%252Bco%252FqSI0%253D&Expires=1605682556)]](https://img.uj5u.com/2020/11/18/194310181050363.png)
這種方案可以說是針對上一個方案改進的,效率也會提高很多,
2.4 狀態機冪
這種機制適用于有不同狀態的業務,moon的上一家公司就是這樣做的,
我們的訂單系統,一條訂單會有多個狀態,如:待付款,鎖定,已付款等狀態,而這些狀態都是有流程和邏輯的,我們可以根據這個狀態判斷是否執行后續業務操作,
2.5 樂觀鎖(更新操作)
就是資料庫中增加版本號欄位,每次更新根據版本號來判斷
程序如下:
- 首先客戶端先請求服務端,先查詢出當前的version版本,
- select version from … where …
- 根據version版本來做sql操作
- UPDATE … SET … version=(version+1) WHERE … AND version=version;
這個圖示我就不再畫了,還是比較簡單的
2.6 悲觀鎖(更新操作)
假設每一次拿資料,都有認為會被修改,所以給資料庫的行上鎖,也是基于資料庫特性來完成,
當資料庫執行select for update時會獲取被select中的資料行的行鎖,因此其他并發執行的select for update如果試圖選中同一行則會發生排斥(需要等待行鎖被釋放),因此達到鎖的效果,
START TRANSACTION; # 開啟事務
SELETE * FROM TABLE WHERE .. FOR UPDATE;
UPDATE TABLE SET ... WHERE ..;
COMMIT; # 提交事務
結語
關于介面冪等性這部分內容,解決方案其實大同小異,很多方式的原理都是一樣的,更多的其實都是在業務鏈路中去過濾,也會有很多是有訊息中間件去解決的,默認在中間件這一層就直接過濾掉了,當然每種方式都有各自的優點和缺點,需要結合當前的業務去選擇,今天的文章內容,你get到了嗎?
我是moon,下一篇,真的要講jvm了~ 下次見~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/223765.html
標籤:其他
上一篇:C#Datatable
下一篇:entity物體類
