2009-11-22 58 views
5

我們有三個Web服務(/a/b/c),其中每個服務映射到一個單獨的Java類(ClassAClassBClassC)的方法(go())。如何防止Web服務API中的併發?

只有一個服務應該同時運行(即:/b無法運行,而/a正在運行)。然而,因爲這是一個REST API,所以沒有什麼可以阻止客戶端請求同時運行服務。

什麼是服務器上最好的,最簡單的方法執行的服務同時運行?


更新:這是一個內部應用程序,我們將不會有大的負荷,將只有一個單一的應用程序服務器。

更新:這是一個主觀問題,因爲您可以對影響最終答案的一般應用程序設計作出不同的論點。接受overthink的答案,因爲我發現最有趣和有益的。

+2

@Marcus:它可能是一個內部應用程序,但有內在的侷限性,在這個階段是一個壞主意,設計的東西。你可能會認爲它永遠不需要擴展,但你能確定嗎?把自己的頭痛保存下來,堅持一些最佳做法! – jkp 2009-11-22 03:52:54

+0

@jkp,採取點。 – 2009-11-22 05:35:51

回答

6

假設這也不行,只是迫使Web服務器只有一個監聽線程服務請求...我想我只是用一個靜態的鎖(ReentrantLock可能,爲清楚起見,儘管你可以任意共享對象同步,真的):

public class Global { 
    public static final Lock webLock = new ReentrantLock(); 
} 

public class ClassA { 
    public void go() { 
     Global.webLock.lock() 
     try { 
      // do A stuff 
     } finally { 
      Global.webLock.unlock() 
     } 
    } 
} 

public class ClassB { 
    public void go() { 
     Global.webLock.lock() 
     try { 
      // do B stuff 
     } finally { 
      Global.webLock.unlock() 
     } 
    } 
} 

public class ClassC { 
    public void go() { 
     Global.webLock.lock() 
     try { 
      // do C stuff 
     } finally { 
      Global.webLock.unlock() 
     } 
    } 
} 
+0

@overthink:當他想要更多的服務層時會發生什麼?如果鎖在這些層上實施,則此解決方案不會擴展。 – jkp 2009-11-22 03:48:42

+1

@overthink:所以你說,你也可以使用,而不是'Global.webLock.lock()/解鎖()''語句同步(共享對象){...}'塊? – 2009-11-22 05:36:33

+1

@Marcus:是的,你可以使用同步而不是鎖定/解鎖。例如在我上面的示例中,您可以使'webLock'成爲一個簡單的'Object',並使用'synchronized(webLock){...}'代替。 – overthink 2009-11-23 01:43:05

3

首先,如果您不得不對WebService層實施併發限制,您可能會遇到問題,但不知道您的體系結構。雖然您可以使用傳統的鎖等串行化兩種服務的請求,但當您添加第二個Web層來擴展您的解決方案時會發生什麼?如果這些鎖對於Web層是本地的,那麼它們將是無用的。

我猜可能有一層位於Web服務下面的某種類型的層,它在這裏您需要強制執行這些限制。如果客戶端B在客戶端A發出衝突請求後進入,則後端在發現狀態發生變化時應拒絕該請求,然後應該將409返回給第二個客戶端。在最後的比賽條件仍然有可能,但你必須讓你的最低公共層保護你免受衝突的要求。

0

您可以使用某種信號量來保持跨服務序列的訪問。

6

您的設計是有缺陷的。這些服務應該是冪等的。如果您擁有的課程不支持這些課程,請重新設計,直到他們完成。聽起來,三種方法中的每一種都應該成爲服務的基礎,而不是類。

+0

你能詳細說明一下嗎? – 2009-11-22 04:04:02

+0

我可以看到防止go()方法的類A,B和C的同時運行的唯一原因是,他們分享一些東西。如果這是班級數據或數據庫數據,則應該嘗試重新設計,以免共享任何內容。這將允許他們同時運行,而不用擔心干擾。如果這是不可能的,那麼你應該有一個服務,以正確的順序調用它們,以確保它們不會失序。在任何一種情況下,我認爲你的設計是有缺陷的,治癒將比疾病更糟糕。 – duffymo 2009-11-22 04:09:06

+0

+100,如果我可以。設計中的某個地方存在很大的問題,信號量,鎖等不是解決這個問題的方法。 – 2009-11-22 13:07:22

0

爲什麼不使用超媒體來限制訪問?

使用類似,

POST /A 

到initate第一個過程。它是完整的,當結果應提供一個鏈接跟隨啓動第二過程,

<ResultsOfProcessA> 
    <Status>Complete</Status> 
    <ProcessB href="/B"/> 
</ResultsOfProcessA> 

按照鏈接initate第二過程,

POST /B 

,並重復部分C.

可以說一個行爲不當的客戶端可以緩存鏈接步驟B和嘗試重新使用它在未來有一定的要求,以規避序列。但是,在執行步驟A時分配某種令牌不會太困難,並且需要將令牌傳遞給步驟B和C以阻止客戶端手動構建URL。

進一步閱讀你的意見,似乎你有一種情況,A可以在B之前或之後運行。在這種情況下,我會建議創建一個資源D來表示整個過程集(A, B和C)。當一個客戶端檢索到D時,它會顯示允許遵循的URI。一旦客戶端啓動了A進程,那麼D資源應該在處理期間移除B鏈接。當B是A開始之前應該出現相反

這項技術的另一個優點是,它是明顯的,如果A或B已經運行了一天的狀態可以在D.顯示一旦A和B具有已經運行,則D可以包含用於C的鏈接。

超媒體並不是100%萬無一失的解決方案,因爲您可以擁有兩個具有相同D副本的客戶端,並且都可能認爲過程A尚未運行,試圖同時運行它。這可以通過在D上具有某種「上次修改」時間戳來解決,並且每當D的狀態改變時您都可以更新該時間戳。這可以允許稍後的請求被拒絕。根據您的場景描述,看起來這更像是一個邊緣案例,超媒體將捕獲大多數並行運行進程的嘗試。