编程中的幂等性(一):http幂等性
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                编程中的幂等性(一):http幂等性
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                
                            
                            
                            一、什么是冪等性?什么是冪等操作?
冪等:是一個數學概念,表示N次變換和1次變換的結果相同。
冪等操作:其特點是任意多次執行所產生的影響均與一次執行的影響相同(不會改變資源狀態,對數據沒有副作用)。
冪等性:一系列操作都是冪等操作。
冪等接口:冪等接口認為,外部調用者會存在多次調用的場景,為了防止重試對數據狀態的改變,需要將接口的設計為冪等的
HTTP方法的冪等性是指一次和多次請求某一個資源應該具有同樣的副作用。說白了就是,同一個請求,發送一次和發送N次效果是一樣的!冪等性是分布式系統設計中十分重要的概念,而HTTP的分布式本質也決定了它在HTTP中具有重要地位。下面將以HTTP中的冪等性做例子加以介紹。
##簡單場景舉例 假設有一個從賬戶取錢的遠程API(可以是HTTP的,也可以不是),我們暫時用類函數的方式記為:
withdraw的語義是從account_id對應的賬戶中扣除amount數額的錢;如果扣除成功則返回true,賬戶余額減少amount;如果扣除失敗則返回false,賬戶余額不變。 值得注意的是:和本地環境相比,我們不能輕易假設分布式環境的可靠性。 所以問題來了,一種典型的情況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果由于網絡等原因被掉丟了,導致客戶端無法得知處理結果。如果是在網頁上,一些不恰當的設計可能會使用戶認為上一次操作失敗了,然后刷新頁面,這就導致了withdraw被調用兩次,賬戶也被多扣了一次錢。如圖所示:
解決方案一:采用分布式事務,通過引入支持分布式事務的中間件來保證withdraw功能的事務性。分布式事務的優點是對于調用者很簡單,復雜性都交給了中間件來管理。 缺點則是一方面架構太重量級,容易被綁在特定的中間件上,不利于異構系統的集成(個人理解:中間件容易形成瓶頸); 另一方面分布式事務雖然能保證事務的ACID性質,而但卻無法提供性能和可用性的保證(個人理解:影響系統的并發性能及工作效率)。
解決方案二:冪等設計。我們可以通過一些技巧把withdraw變成冪等的,比如:
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是冪等的,所以任何一步由于網絡等原因失敗或超時,客戶端都可以重試,直到獲得結果。如圖所示:
和分布式事務相比,冪等設計的優勢在于它的輕量級,容易適應異構環境,以及性能和可用性方面。在某些性能要求比較高的應用,冪等設計往往是唯一的選擇。
★★HTTP的冪等性介紹 本文主要以HTTP GET、DELETE、PUT、POST四種方法為主進行語義和冪等性的介紹。 HTTP GET方法用于獲取資源,不應有副作用,所以是冪等的。比如:GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次還是N次都沒有副作用。請注意,這里強調的是一次和N次具有相同的副作用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次得到不同的結果,但它本身并沒有產生任何副作用,因而是滿足冪等性的。 HTTP DELETE方法用于刪除資源,有副作用,但它應該滿足冪等性。比如:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的副作用是相同的,即刪掉id為4231的帖子;因此,調用者可以多次調用或刷新頁面而不必擔心引起錯誤。 HTTP POST方法用于創建資源,所對應的URI并非創建的資源本身,而是去執行創建動作的操作者,有副作用,不滿足冪等性。比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下創建一篇帖子,HTTP響應中應包含帖子的創建狀態以及帖子的URI。兩次相同的POST請求會在服務器端創建兩份資源,它們具有不同的URI;所以,POST方法不具備冪等性。 HTTP PUT方法用于創建或更新操作,所對應的URI是要創建或更新的資源本身,有副作用,它應該滿足冪等性。比如:PUT http://www.forum/articles/4231的語義是創建或更新ID為4231的帖子。對同一URI進行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有冪等性。
★★對前文示例進行改進(取款冪等性設計) 利用Web API的形式實現前面所提到的取款功能。 1、用POST /tickets來實現create_ticket; 2、用PUT /accounts/account_id/ticket_id&amount=xxx來實現idempotent_withdraw。 值得注意的是嚴格來講amount參數不應該作為URI的一部分,真正的URI應該是/accounts/account_id/ticket_id,而amount應該放在請求的body中。這種模式可以應用于很多場合,比如:論壇網站中防止意外的重復發帖。
★★電商中遇到的問題 ★如何防范 POST 重復提交 HTTP POST 操作既不是安全的,也不是冪等的(至少在HTTP規范里沒有保證)。當我們因為反復刷新瀏覽器導致多次提交表單,多次發出同樣的POST請求,導致遠端服務器重復創建出了資源。 所以,對于電商應用來說,第一對應的后端 WebService 一定要做到冪等性,第二服務器端收到 POST 請求,在操作成功后必須302跳轉到另外一個頁面,這樣即使用戶刷新頁面,也不會重復提交表單。 ★把分布式事務分解為具有冪等性的異步消息處理 電商的很多業務,考慮更多的是 BASE(即Basically Available、Soft state、和Eventually consistent),而不是 ACID(Atomicity、Consistency、Isolation和 Durability)。即為了滿足高負載的用戶訪問,我們可以容忍短暫的數據不一致。那怎么做呢? 第一,不做分布式事務,代價太大。第二,不一定需要實時一致性,只需要保證最終的一致性即可。第三,“通過狀態機和嚴格的有序操作,來最大限度地降低不一致性”。第四,最終一致性(Eventually Consistent)通過異步事件做到。 如果消息具有操作冪等性,也就是一個消息被應用多次與應用一次產生的效果是一樣的話,那么把不需要同步執行的事務交給異步消息推送和訂閱者集群來處理即可。假如消息處理失敗,那么就消息重播,由于冪等性,應用多次也能產生正確的結果。 實際情況下,消息很難具有冪等性,解決方法是使用另一個表記錄已經被成功應用的消息,即消息隊列和消息應用狀態表一起來解決問題。 ★★總結 上面簡單介紹了冪等性的概念,用冪等設計取代分布式事務的方法,以及HTTP主要方法的語義和冪等性特征。其實,如果要追根溯源,冪等性是數學中的一個概念,表達的是N次變換與1次變換的結果相同,有興趣的讀者可以從Wikipedia上進一步了解。
                            
                        
                        
                        ##簡單場景舉例 假設有一個從賬戶取錢的遠程API(可以是HTTP的,也可以不是),我們暫時用類函數的方式記為:
| 1 | bool withdraw(account_id, amount) | 
withdraw的語義是從account_id對應的賬戶中扣除amount數額的錢;如果扣除成功則返回true,賬戶余額減少amount;如果扣除失敗則返回false,賬戶余額不變。 值得注意的是:和本地環境相比,我們不能輕易假設分布式環境的可靠性。 所以問題來了,一種典型的情況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果由于網絡等原因被掉丟了,導致客戶端無法得知處理結果。如果是在網頁上,一些不恰當的設計可能會使用戶認為上一次操作失敗了,然后刷新頁面,這就導致了withdraw被調用兩次,賬戶也被多扣了一次錢。如圖所示:
解決方案一:采用分布式事務,通過引入支持分布式事務的中間件來保證withdraw功能的事務性。分布式事務的優點是對于調用者很簡單,復雜性都交給了中間件來管理。 缺點則是一方面架構太重量級,容易被綁在特定的中間件上,不利于異構系統的集成(個人理解:中間件容易形成瓶頸); 另一方面分布式事務雖然能保證事務的ACID性質,而但卻無法提供性能和可用性的保證(個人理解:影響系統的并發性能及工作效率)。
解決方案二:冪等設計。我們可以通過一些技巧把withdraw變成冪等的,比如:
| 123 | 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是冪等的,所以任何一步由于網絡等原因失敗或超時,客戶端都可以重試,直到獲得結果。如圖所示:
和分布式事務相比,冪等設計的優勢在于它的輕量級,容易適應異構環境,以及性能和可用性方面。在某些性能要求比較高的應用,冪等設計往往是唯一的選擇。
★★HTTP的冪等性介紹 本文主要以HTTP GET、DELETE、PUT、POST四種方法為主進行語義和冪等性的介紹。 HTTP GET方法用于獲取資源,不應有副作用,所以是冪等的。比如:GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次還是N次都沒有副作用。請注意,這里強調的是一次和N次具有相同的副作用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次得到不同的結果,但它本身并沒有產生任何副作用,因而是滿足冪等性的。 HTTP DELETE方法用于刪除資源,有副作用,但它應該滿足冪等性。比如:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的副作用是相同的,即刪掉id為4231的帖子;因此,調用者可以多次調用或刷新頁面而不必擔心引起錯誤。 HTTP POST方法用于創建資源,所對應的URI并非創建的資源本身,而是去執行創建動作的操作者,有副作用,不滿足冪等性。比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下創建一篇帖子,HTTP響應中應包含帖子的創建狀態以及帖子的URI。兩次相同的POST請求會在服務器端創建兩份資源,它們具有不同的URI;所以,POST方法不具備冪等性。 HTTP PUT方法用于創建或更新操作,所對應的URI是要創建或更新的資源本身,有副作用,它應該滿足冪等性。比如:PUT http://www.forum/articles/4231的語義是創建或更新ID為4231的帖子。對同一URI進行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有冪等性。
★★對前文示例進行改進(取款冪等性設計) 利用Web API的形式實現前面所提到的取款功能。 1、用POST /tickets來實現create_ticket; 2、用PUT /accounts/account_id/ticket_id&amount=xxx來實現idempotent_withdraw。 值得注意的是嚴格來講amount參數不應該作為URI的一部分,真正的URI應該是/accounts/account_id/ticket_id,而amount應該放在請求的body中。這種模式可以應用于很多場合,比如:論壇網站中防止意外的重復發帖。
★★電商中遇到的問題 ★如何防范 POST 重復提交 HTTP POST 操作既不是安全的,也不是冪等的(至少在HTTP規范里沒有保證)。當我們因為反復刷新瀏覽器導致多次提交表單,多次發出同樣的POST請求,導致遠端服務器重復創建出了資源。 所以,對于電商應用來說,第一對應的后端 WebService 一定要做到冪等性,第二服務器端收到 POST 請求,在操作成功后必須302跳轉到另外一個頁面,這樣即使用戶刷新頁面,也不會重復提交表單。 ★把分布式事務分解為具有冪等性的異步消息處理 電商的很多業務,考慮更多的是 BASE(即Basically Available、Soft state、和Eventually consistent),而不是 ACID(Atomicity、Consistency、Isolation和 Durability)。即為了滿足高負載的用戶訪問,我們可以容忍短暫的數據不一致。那怎么做呢? 第一,不做分布式事務,代價太大。第二,不一定需要實時一致性,只需要保證最終的一致性即可。第三,“通過狀態機和嚴格的有序操作,來最大限度地降低不一致性”。第四,最終一致性(Eventually Consistent)通過異步事件做到。 如果消息具有操作冪等性,也就是一個消息被應用多次與應用一次產生的效果是一樣的話,那么把不需要同步執行的事務交給異步消息推送和訂閱者集群來處理即可。假如消息處理失敗,那么就消息重播,由于冪等性,應用多次也能產生正確的結果。 實際情況下,消息很難具有冪等性,解決方法是使用另一個表記錄已經被成功應用的消息,即消息隊列和消息應用狀態表一起來解決問題。 ★★總結 上面簡單介紹了冪等性的概念,用冪等設計取代分布式事務的方法,以及HTTP主要方法的語義和冪等性特征。其實,如果要追根溯源,冪等性是數學中的一個概念,表達的是N次變換與1次變換的結果相同,有興趣的讀者可以從Wikipedia上進一步了解。
總結
以上是生活随笔為你收集整理的编程中的幂等性(一):http幂等性的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: protel99se的封装库
- 下一篇: ESP32学习笔记(46)——MQTT客
