什麼是幂等(Idempotency)?簡單來說,一個操作如果具有任意多次執行所産生的影響均與一次執行的影響相同,我們就稱之為幂等。
這樣說來,似乎很容易理解。但要知道,這樣的定義,其實是一個語義範疇對行為結果的定義。如何用語法和規則去确保行為能達到這個結果,往往需要很謹慎的設計和實現。實際系統中,幂等是一個極為重要的概念。無論是在大型互聯網應用還是企業級架構中,我們都見到 REST API 被越來越多的采用。而正确實現幂等,往往是 API 中最難的技術點之一。
先說為什麼重要。舉一個簡單易懂的例子。
比如你要處理一次電商網站收款或者付款的交易。當你給微信支付發送這個付款請求後,一個順利的場景,是沒有任何錯誤發生,微信支付收到你的付款請求,處理所有轉賬,然後返回一個 HTTP 200 消息表示交易完成。
那如果發出請求後,有個請求超時,你再也沒有收到關于這個請求是成功還是失敗的的回執,又該如何呢?
這裡就有很多種可能情況:
這個請求在到達微信支付端前就已經發生超時,微信支付從來沒有收到這樣的請求。
這個請求到達微信支付端,但是支付交易失敗,這時發生超時,微信支付收到這樣的請求,但沒有處理成功。
這個請求到達微信支付端,并且支付交易成功,這時發生超時,微信支付收到這樣的請求,處理成功,但是沒有回執。
這個請求到達微信支付端,并且支付交易成功,并且發回回執,然而因為網絡原因回執丢失,客戶端超時,微信支付收到這樣的請求,處理成功,發出回執,但是客戶沒有收到。
很直觀的一個想法,也是現實中用戶最常見的做法,是重新提交一次支付請求。但是這樣就有一個潛在的問題:請求超時是上面的哪一種情況?會不會引發多次支付的可能性?
這就涉及到系統中的幂等是如何實現的了。
那麼幂等又該如何實現呢?“多次執行所産生的影響均與一次執行的影響相同”,簡而言之,我們需要一個 Dedup(去重)的機制。這往往有很多不同的實現方法,但是有兩個很關鍵的因素:
一是 Idempotency Key(幂等令牌)。也就是客戶端和服務器端通過什麼來識别這實際上是同一個請求,或是同一個請求的多次 retry(嘗試)。這往往需要雙方又一個既定的協議。往往是類似賬單号或者交易 token(令牌)這樣一個可以唯一标識同一個請求意願的元素。通常由客戶端生成。
二是 Uniqueness Guarantee(确保唯一性)。服務器端用什麼機制去确保同一個請求一定不會被處理兩次,也就是微信支付怎麼确保同一筆交易不會因為客戶端發送兩次請求就被處理多次。最通常的做法是利用數據庫。比如把幂等令牌所在的數據庫表的 Column(列)作為 unique indexed。這樣,當你試圖存儲兩個含有同樣令牌的請求時,必定有一個會報錯。注意,簡單的讀檢查并不一定行,因為讀與讀之間會有 Race Condition(競争條件),因此還是有可能出錯。
如果一個系統可以正确的處理和實現上面的兩個要素,那麼基本就能達到幂等的需求。那麼現實系統中常見的問題都出在哪裡呢?
一是幂等令牌什麼時候産生,怎麼産生?這一點很重要。拿上面的例子來說。就算微信支付可以保證每一個請求對應的支付交易一定隻會被處理一次。但是這個請求的多次重複,一定要共有某一個微信可以識别的标識。假如客戶端對同一筆交易的多次請求,産生的幂等令牌并不相同,那不論你别的地方多麼完美,都沒有可能保證 “一個操作如果具有任意多次執行所産生的影響均與一次執行的影響相同”。
二是有沒有令牌被誤删的可能。這是上面的問題的一個特殊情況。幂等令牌是由客戶端生成的。那麼如果生成的令牌在被使用後(一次微信支付請求中使用了),不小心因為 DB rollback 等原因被删除了。那麼客戶端就不知道自己其實已經發過一次請求。就有可能生成一個新的賬單,并産生全新的令牌,而服務端将對此一無所知。
三是各種競争條件。上面說的用 DB 讀來确保唯一性經常因為競争而不工作。其實一個需要幂等的系統中,保證唯一性的各個環節和實現,都要考慮 Race Condition。
四是對請求 Retry 的處理。這大部分是服務器端要做的。一個常見的方法是區分正在處理的請求、和處理成功、處理失敗的請求。這樣當客戶端重試的時候,根據情況或者直接返回,或者再次處理。就好像前面說的微信支付的例子。微信支付服務上,需要知道每一筆交易的處理情況,才能正确處理在此轉賬請求時,是不是需要進行任何動作。
五是一個系統中需要多層幂等。什麼意思呢?A 發送請求給 B,B 處理的一部分是要發送請求給另一個系統 C,C 在處理的過程中還可能需要發請求給另一個系統 D…… D 處理完了返回給 C,C 返回給 B,B 返回給 A。在這個鍊條中,如果 A B C D 中任何一個系統并沒有正确實現幂等,也就是出現了 “幂等漏洞”,那麼一個請求還是有可能被多次執行,産生區别于一次執行的影響。
在回到本文的開頭,什麼是幂等?一個操作如果具有任意多次執行所産生的影響均與一次執行的影響相同,我們就稱之為幂等。
這樣的語義範疇對行為結果的定義,隻有當你的實現中所有的細節都做對了,你才能得到想要的效果。任何一個地方設計有漏洞,或是實現有 Bug,那還是不成。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!