一、什么是冪等性
在編程中一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。
HTTP方法的冪等性是指一次和多次請求某一個資源應該具有同樣的副作用。冪等性屬於語義范疇,正如編譯器只能幫助檢查語法錯誤一樣,HTTP規范也沒有辦法通過消息格式等語法手段來定義它.
二、為什么需要冪等性
舉個例子,假設有一個從賬戶中取錢的api。
bool withdraw(account_id, amount)
withdraw的語義是從account_id對應的賬戶中扣除amount數額的錢;如果扣除成功則返回true,賬戶余額減少amount;如果扣除失敗則返回false,賬戶余額不變
一種典型的情況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果由於網絡等原因被掉丟了,導致客戶端無法得知處理結果。如果是在網頁上,一些不恰當的設計可能會使用戶認為上一次操作失敗了,然后刷新頁面,這就導致了withdraw被調用兩次,賬戶也被多扣了一次錢。如圖1所示:

三、如何通過冪等設計來解決?
我們可以通過一些技巧把withdraw變成冪等的,比如:
int create_ticket() bool idempotent_withdraw(ticket_id, account_id, amount)
create_ticket的語義是獲取一個服務器端生成的唯一的處理號ticket_id,它將用於標識后續的操作。idempotent_withdraw和withdraw的區別在於關聯了一個ticket_id,一個ticket_id表示的操作至多只會被處理一次,每次調用都將返回第一次調用時的處理結果。這樣,idempotent_withdraw就符合冪等性了,客戶端就可以放心地多次調用。
基於冪等性的解決方案中一個完整的取錢流程被分解成了兩個步驟:1.調用create_ticket()獲取ticket_id;2.調用idempotent_withdraw(ticket_id, account_id, amount)。雖然create_ticket不是冪等的,但在這種設計下,它對系統狀態的影響可以忽略,加上idempotent_withdraw是冪等的,所以任何一步由於網絡等原因失敗或超時,客戶端都可以重試,直到獲得結果。如圖2所示:

