轉載:http://m.blog.csdn.net/loveblog1314/article/details/72649809
什么是冪等性
抄用一段數學上的定義:f(f(x)) = f(x)。x被函數f作用一次和作用無限次的結果是一樣的。冪等性應用在軟件系統中,我把它簡單定義為:某個函數或者某個接口使用相同參數調用一次或者無限次,其造成的后果是一樣的,在實際應用中一般針對於接口進行冪等性設計。舉個栗子,在系統中,調用方A調用系統B的接口進行用戶的扣費操作時,由於網絡不穩定,A重試了N次該請求,那么不管B是否接收到多少次請求,都應該保證只會扣除該用戶一次費用。
加深對冪等性的了解
冪等性一般應用於協議設計,TCP協議支持冪等嗎?答案是肯定的,在網絡不穩定時,操作系統可以肆無忌憚的重發TCP報文片段。TCP協議能夠保證冪等的核心在於sequence number字段,一個序列號的在較長的一段時間內均不會出現重復。對於應用層的協議設計,原理和TCP是類似的,我們需要一個不重復的序列號。再簡單一點說,在一個業務流程的處理中,我們需要一個不重復的業務流水號,以保證冪等性。
舉個實際應用場景:用戶A在網頁上發起一筆游戲充值請求,瀏覽器引導用戶去銀行支付,支付成功后系統給用戶進行充值。
協議設計上,我們通過全局唯一的充值訂單號貫穿整個業務流程,使該業務支持冪等。
我們先從一個簡單的程序理解一下冪等性:
public class Main { private int i = 0; //這個方法不具有冪等性,每調用一次,它就會改變Main的狀態(即改變了i) public void idempotent() { i++; } //冪等性,無論這個方法調用多少次,它都不會改變Main類的狀態。 public void simple() { System.out.println(i); } }看完這些,你似乎對冪等性有了更深的了解。那么冪等性問題會出現在哪些場景呢?
電商,第三方支付,搶紅包等場景。
這些應用場景,你似乎看到了他們的共同特征。對,那就是高並發。
冪等控制的實現
HTTP的冪等性
冪等表示:請求服務器一次或是多次,返回的結果均是一樣的【select 】一般是GET請求
非冪等表示:請求服務器不同的次數,返回的結果將是不一樣的[update delete] 一般是POST請求
HTTP協議本身是一種面向資源的應用層協議,但對HTTP協議的使用實際上存在着兩種不同的方式:一種是restful,它把HTTP當成應用層協議,另一種是SOA,它並沒有完全把HTTP當成應用層協議,而是把HTTP協議作為了傳輸層協議,然后在HTTP之上建立了自己的應用層協議。
restful風格,想了解的可以去看看webservice編程,這里不是本文的主題。
本文所討論的HTTP冪等性主要針對RESTful風格的,不過正如上一節所看到的那樣,冪等性並不屬於特定的協議,它是分布式系統的一種特性;所以,不論是SOA還是RESTful的Web API設計都應該考慮冪等性。
重要方法 | 安全 | 冪等 |
GET | 是 | 是 |
POST | 否 | 否 |
PUT | 否 | 是 |
DELETE | 否 | 是 |
數據庫冪等
數據庫上的冪等和事務是一體的。
1. 查詢操作
查詢一次和查詢多次,在數據不變的情況下,查詢結果是一樣的。select是天然的冪等操作
2. 刪除操作
刪除操作也是冪等的,刪除一次和多次刪除都是把數據刪除。(注意可能返回結果不一樣,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個)
3.唯一索引,防止新增臟數據
比如:支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎么防止給用戶創建資金賬戶多個,那么給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄
4.悲觀鎖
查詢一次和查詢多次,在數據不變的情況下,查詢結果是一樣的。select是天然的冪等操作
2. 刪除操作
刪除操作也是冪等的,刪除一次和多次刪除都是把數據刪除。(注意可能返回結果不一樣,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個)
3.唯一索引,防止新增臟數據
比如:支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎么防止給用戶創建資金賬戶多個,那么給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄
獲取數據的時候加鎖獲取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主鍵或者唯一索引,不然是鎖表,會死人的
悲觀鎖使用時一般伴隨事務一起使用,數據鎖定時間可能會很長,根據實際情況選用
5. 樂觀鎖
樂觀鎖只是在更新數據那一刻鎖表,其他時間不鎖表,所以相對於悲觀鎖,效率更高。
客戶端冪等控制機制-token
頁面的數據只能被點擊提交一次
發生原因:
由於重復點擊或者網絡重發,或者nginx重發等情況會導致數據被重復提交
解決辦法:
集群環境:采用token加redis(redis單線程的,處理需要排隊)
單JVM環境:采用token加redis或token加jvm內存
處理流程:
1. 數據提交前要向服務的申請token,token放到redis或jvm內存,token有效時間
2. 提交后后台校驗token,同時刪除token,生成新的token返回
token特點:
要申請,一次有效性,可以限流