2016-07-30 58 views
0

我現在正在兩個WebApi項目中工作,在這兩個項目中我們遇到了同樣的問題:我們有一些操作Actions,如果正在執行操作A,則無法執行操作,反之亦然。因此,例如,如果Actions中的任何一個正在運行,並且用戶發送請求到動作A,那麼動作A必須等待其他動作完成後才能運行。如何處理RESTful服務中的互斥操作?

從有狀態服務的背景來看,我們的第一個想法是使用鎖,雖然有很大的阻力。我們創建了一個單一服務,只需將object映射到用戶標識,並將其用作鎖。

現在,當然這會帶來一些非常棘手的問題,如:

(1)我們必須強迫用戶一直跟到只有一個服務器實例,甚至當他們的請求都從不同的設備進來世界各地不同,因爲鎖不會自動跨越一個實例。

(2)然後,我們將不得不建立我們自己的[次優]負載均衡器。我們的計劃是使用Azure的內置版本。

所以我的問題是:你如何優雅地處理這個問題的方式,不會損害水平可伸縮性,並且最好不需要自定義負載平衡策略?

回答

3

儘管當然不是唯一的方法,但它是排隊請求並用202(Accepted)響應。通常,響應還會提供一個URL,然後客戶端可以通過該URL來確定未完成請求的狀態。同樣,如果需要取消掛起的操作,客戶端可以刪除該URL。

編輯:把上述的說明另一種方式中,假設每一個修改的共享資源請求被處理如下:

  1. 客戶發出請求
  2. 前端服務器(S)轉發向後端服務器請求排隊等待處理(不需要鎖定)。除了明顯的拒絕(畸形請求,未授權訪問等)外,前端服務器始終接受請求。實際請求是否成功或實際處理請求的時間對前端服務器而言並不重要。

(現在,假設客戶端實際上需要知道請求的結果...)

  • 前端服務器返回202(已接受)響應,其中包括一個URL。 URL有效地指向相關請求的狀態(可能是結果)。
  • 客戶端定期輪詢URL。 (前端服務器剛剛讀取後端狀態,因此它應該很快並且不需要鎖定。)
  • 最終,後端服務器將處理實際請求並根據需要更新請求狀態。
  • 客戶端獲取更新狀態並相應地執行操作。
  • 這裏的關鍵點是:

    • 鎖並不需要的大部分時間。當他們需要時,他們 應該只用於非常快的操作。
    • 請求的實際處理在後端序列化。這避免了併發問題。
    • 前端上的負載平衡和縮放問題被最小化,因爲它們仍然只處理後端資源。前端服務器不需要與除後臺之外的任何其他設備協調。

    雖然與原始問題沒有密切關係,但這種方法的其他優點之一是「排隊」請求有效地成爲共享資源的事務歷史記錄。

    +0

    但是,通過排隊不會遇到與鎖定相同的問題 - 也就是說,不得不迫使用戶只使用一個服務器實例,或者不得不繞過實例傳遞狀態?使用選項1,我必須構建自己的負載平衡器。選項2會產生許多不同的問題,可能會佔用過多的帶寬。 –

    +0

    我已經更新了答案,有點更詳細。誠然,它仍然是相當通用的,但希望它能爲您提供足夠的解釋,以確定您是否可以將其應用於您的特定情況。 – seairth

    +0

    「隊列」是否可能是一個數據庫,以便可以從多個負載平衡服務器訪問請求及其狀態? –

    1

    只要所有服務器都使用一些並行控制機制,可以同時排除正在運行的關鍵操作,就可以路由到任何服務器。該機制可能是分佈式鎖(zookeeper,etcd,redis,hazelcast等)或集中式鎖(例如用於鎖定的特定服務器,db,調優的memcached集羣)。然後,您的服務器可以等待在等待鎖定時響應REST客戶端,或者您可以實施seairth的答案中建議的策略。我不會說它根本不會影響可擴展性,但是如果關鍵部分很短並且很少發生衝突,那麼接收請求的所有服務器大部分時間都會很忙。