冪等和高併發在電商系統中的使用

在Java web項目開發中,常常會聽到在作訂單系統中生成訂單的時候,要作冪等性控制和併發控制,特對此部份內容做出總結,在高併發場景下,代碼層面須要實現併發控制;可是冪等性,其實更多的是系統的接口對外的一種承諾,承諾一次請求和屢次請求會返回一樣的數據。關於冪等性將分別從高等代數中的冪等性、HTTP中的冪等性和訂單生成系統中的冪等性闡述;併發性控制則提供了分佈式鎖等方式來對併發場景進行代碼實現。html

1、冪等性 idempotence  ['aɪdəmpoʊtəns]web

1.高等代數中關於冪等idempotence概念解釋:ajax

  單目運算, x爲某集合內的任意數, 若是知足f(x)=f(f(x)), 那麼咱們稱f運算爲具備冪等性(idempotent)。好比在實數集中,絕對值運算就是一個例子: abs(a)=abs(abs(a))。redis

  雙目運算,x爲某集合內的任意數, f爲運算子若是知足f(x,x)=x, f運算的前提是兩個參數都同爲x, 那麼咱們也稱f運算爲具備冪等性。好比在實數集中,求兩個數的最大值的函數: max(x,x) = x, 還有布爾代數中,邏輯運算 "與", "或" 也都是冪等運算, 由於他們符合AND(0,0) = 0, AND(1,1) = 1, OR(0,0) = 0, OR(1,1) = 1。後端

在將冪等性應用到軟件開發中,須要一些更深的理解,個人理解以下:數學處理的是運算和數值, 程序開發中每每處理的是對象和函數. 可是咱們不能簡單地理解爲數學冪等中的運算就是函數,而數值就是對象。如Person對象有兩個屬性weight和age,可是全部的function只能對其中一個屬性操做。因此從這個層面咱們能夠理解爲: 函數只對該函數所操做的對象某個屬性具備冪等性, 而不是說對整個對象有運算冪等性。 api

Person { 
    private int weight; 
    private int age; 
    //是冪等函數
    public void setAge(int v){ 
        this.age = v; 
    }
    //不是冪等函數
    public void increaseAge(){ 
        this.age++;
    } 
    //是冪等函數
    public void setWeight(int v){ 
        this.weight=v+10;//故意加10斤!!
    }
}

還有一點必需要澄清的是: 冪等性所表達的概念關注的是數學層面的運算和數值, 並無說起到數值的安全性問題.如上面的Person的setAge函數, 有兩種case不是冪等性所關心的, 但程序開發卻又必需要關心的:瀏覽器

  • 兩個線程同時調用
  • 由於age從業務上講不可能遞減, 若是前一次調用設置是30歲, 後一次調用變成了10歲或是更離譜的 -1 歲

冪等性是系統的接口對外一種承諾(而不是實現), 承諾只要調用接口成功, 外部屢次調用對系統的影響是一致的。聲明爲冪等的接口會認爲外部調用失敗是常態, 而且失敗以後必然會有重試。因此RESTful設計中將冪等性和安全性做爲兩個不一樣的指標來衡量POST,PUT,GET,DELETE操做的。所以,post不是冪等性的,put get delete都是冪等性的,也即在生成訂單的post請求中,咱們要作冪等性的控制。以下圖,一個ajax請求是一次post請求的示例,若是這個post請求被調用屢次,它會向表插入多條記錄,很顯然post請求並非冪等性的,因此冪等性的控制交由咱們程序中來控制。安全

2.HTTP協議中的冪等性服務器

  項目中中的SOA和restful API接口的流行,都須要應用層HTTP協議的支持,目前的項目結構:Web API + RIA(Rich Internet Applications富互聯網應用),Web API專一於提供業務服務,RIA專一於用戶界面和交互設計,今後兩個領域的分工更加明晰。正如簡單的Java語言並不意味着高質量的Java程序,簡單的HTTP協議也不意味着高質量的Web API。要想設計出高質量的Web API,還須要深刻理解分佈式系統及HTTP協議的特性。在HTTP1.1規範中定義冪等性。restful

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects 
  of N > 0 identical requests is the same as for a single request.

從定義上看,HTTP方法的冪等性是指一次和屢次請求某一個資源應該具備一樣的做用。冪等性是分佈式系統設計中十分重要的概念,而HTTP的分佈式本質也決定了它在HTTP中具備重要地位。好比有這樣一個業務邏輯,假設有一個從帳戶取錢的遠程API(能夠是HTTP的,也能夠不是),咱們暫時定義接口:

bool withdraw(account_id, amount)

withdraw的語義是從account_id對應的帳戶中扣除amount數額的錢;若是扣除成功則返回true,帳戶餘額減小amount;若是扣除失敗則返回false,帳戶餘額不變。值得注意的是:和本地環境相比,咱們不能輕易假設分佈式環境的可靠性。一種典型的狀況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果因爲網絡等緣由被丟掉了,致使客戶端沒法得知處理結果。若是是在網頁上,一些不恰當的設計可能會使用戶認爲上一次操做失敗了,而後刷新頁面,這就致使了withdraw被調用兩次,帳戶也被多扣了一次錢。以下圖所示:

 

這個問題的解決方案一是採用分佈式事務,經過引入支持分佈式事務的中間件來保證withdraw功能的事務性。分佈式事務的優勢是對於調用者很簡單,複雜性都交給了中間件來管理。缺點則是一方面架構過重量級,容易被綁在特定的中間件上,不利於異構系統的集成;另外一方面分佈式事務雖然能保證事務的ACID性質,而但卻沒法提供性能和可用性的保證。

  另外一種更輕量級的解決方案是冪等設計。咱們能夠經過一些技巧把withdraw變成冪等的,好比:

int create_ticket() 
bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的語義是獲取一個服務器端生成的惟一的處理號token,它將用於標識後續的操做。idempotent_withdraw和withdraw的區別在於關聯了一個token,一個token表示的操做至多隻會被處理一次,每次調用都將返回第一次調用時的處理結果。這樣,idempotent_withdraw就符合冪等性了,客戶端就能夠放心地屢次調用。也就是說,屢次點擊提交的時候,附帶提交的還有服務端生成的token,因爲屢次提交帶的是同一個token,因此服務端對於同一個token的post訂單,至多隻會處理一次,因此間接的實現了冪等性的控制

基於冪等性的解決方案中一個完整的取錢流程被分解成了兩個步驟:1.調用create_ticket()獲取token;2.調用idempotent_withdraw(token, account_id, amount)。雖然create_ticket不是冪等的,但在這種設計下,它對系統狀態的影響能夠忽略,加上idempotent_withdraw是冪等的,因此任何一步因爲網絡等緣由失敗或超時,客戶端均可以重試,直到得到結果。如圖2所示:

 

和分佈式事務相比,冪等設計的優點在於它的輕量級,容易適應異構環境,以及性能和可用性方面。在某些性能要求比較高的應用中,冪等設計每每是惟一的選擇。

  1. HTTP GET方法用於獲取資源,不該有反作用,因此是冪等的。好比:GET http://www.bank.com/account/123456,不會改變資源的狀態,不論調用一次仍是N次都沒有反作用。請注意,這裏強調的是一次和N次具備相同的做用,而不是每次GET的結果相同。GET http://www.news.com/latest-news這個HTTP請求可能會每次獲得不一樣的結果,但它自己並無產生任何反作用,於是是知足冪等性的。
  2. HTTP DELETE方法用於刪除資源,有反作用,但它應該知足冪等性。好比:DELETE http://www.forum.com/article/4231,調用一次和N次對系統產生的反作用是相同的,即刪掉id爲4231的帖子;所以,調用者能夠屢次調用或刷新頁面而沒必要擔憂引發錯誤。
  3. 比較容易混淆的是HTTP POST和PUT。POST和PUT的區別容易被簡單地誤認爲「POST表示建立資源,PUT表示更新資源」;而實際上,兩者都可用於建立資源,更爲本質的差異是在冪等性方面。在HTTP規範中對POST和PUT是這樣定義的:
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified 
by the Request-URI in the Request-Line ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain
an entity which describes the status of the request and refers to the new resource, and a Location header. The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource,
the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an
existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所對應的URI並不是建立的資源自己,而是資源的接收者。好比:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下建立一篇帖子,HTTP響應中應包含帖子的建立狀態以及帖子的URI。兩次相同的POST請求會在服務器端建立兩份資源,它們具備不一樣的URI;因此,POST方法不具有冪等性。而PUT所對應的URI是要建立或更新的資源自己。好比:PUT http://www.forum/articles/4231的語義是建立或更新ID爲4231的帖子。對同一URI進行屢次PUT的反作用和一次PUT是相同的;所以,PUT方法具備冪等性。論壇網站防止重複發帖和訂單生成都用到token方式的冪等性控制。

3.總結

  在電商系統中,常見問題:如何防範post請求的重複提交?HTTP POST 操做既不是安全的,也不是冪等的。當咱們由於反覆刷新瀏覽器致使屢次提交表單,屢次發出一樣的POST請求,致使遠端服務器重複建立出了資源。因此,對於電商應用來講,第一對應的後端 WebService 必定要作到冪等性,第二服務器端收到 POST 請求,在操做成功後必須跳轉到另一個頁面,這樣即便用戶刷新頁面,也不會重複提交表單

2、高併發

1.分佈式鎖的定義

  分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。在分佈式系統中,經常須要協調他們的動做。若是不一樣的系統或是同一個系統的不一樣主機之間共享了一個或一組資源,那麼訪問這些資源的時候,每每須要互斥來防止彼此干擾來保證一致性,在這種狀況下,便須要使用到分佈式鎖。分佈式鎖是一個在不少環境中很是有用的原語,它是不一樣的系統或是同一個系統的不一樣主機之間互斥操做共享資源的有效方法。如在電商系統中,須要保證整個分佈式系統內,對一個重要事物(訂單,帳戶等)的有效操做線程 ,同一時間內有且只有一個。好比交易中心有N臺服務器,訂單中心有M臺服務器,如何保證一個訂單的同一筆支付處理,一個帳戶的同一筆充值操做是原子性的。

  常見的實現分佈式鎖的服務有:memcache zookeeper redis chubby hazelcast。

2.分佈式鎖實現

  分佈式鎖在分佈式應用當中是要常常用到的,主要是解決分佈式資源訪問衝突的問題。傳統的鎖ReentrantLock在去實現的時候是有問題的,ReentrantLock的lock和unlock要求必須是在同一線程進行,而分佈式應用中,lock和unlock是兩次不相關的請求,所以確定不是同一線程,所以致使沒法使用ReentrantLock。

附:

一、什麼是restful風格的API接口?

http://www.ruanyifeng.com/blog/2014/05/restful_api.html

http://www.cnblogs.com/zhengyun_ustc/archive/2012/11/17/topic2.html

http://www.cnblogs.com/j2eetop/p/4612437.html

http://www.cnblogs.com/weidagang2046/p/exception-handling-principles.html

http://www.cnblogs.com/orange1438/p/4637776.html

相關文章
相關標籤/搜索