2009-11-22 13 views
5

私たちは、別のJavaクラス(ClassAClassBClassC)に3つのWebサービス(/a/b/c)メソッドへの各サービスマップ(go())を持っています。WebサービスAPIの並行性を防ぐには?

サービスは同時に実行する必要があります(/aが実行されている間にを実行できません)。しかし、これはREST APIなので、クライアントがサービスの同時実行を要求するのを防ぐことはできません。サーバ上で最良かつ最も簡単な方法は何

へのサービスが同時にない実行を行うことをを強制?


更新:これは内部アプリです、私たちは大きな負荷を持っていないし、ちょうど単一のアプリケーションサーバを持つことになります。

更新:最終的な回答に影響を与える一般的なアプリケーション設計について異なる議論をすることができるので、これは主観的な質問です。 overthinkの答えを受け入れると、私はそのことが最も面白くて助けになりました。

+2

@Marcus:これは内部アプリケーションである可能性もありますが、この段階で制限が組み込まれたものを設計することは悪い考えです。あなたは*スケールする必要はないと思うかもしれませんが、確信が持てますか?後でラインの下で自分自身を頭痛を保存し、いくつかのベストプラクティスに従ってください! – jkp

+0

@jkp、Point taken。 –

答えて

6

ウェブサーバーに要求を処理するリスニングスレッドが1つしかないようにするのは賢明ではないと思われます...静的なロックを使用しているとします(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

+1

@overthink:あなたは 'Global.webLock.lock()/ unlock()'の代わりに 'synchronized(sharedObject){...}'ブロックを使うことができると言っていますか? –

+1

@Marcus:はい、ロック/ロック解除の代わりに同期を使用できます。例えば上の私の例では、 'webLock'を単純な' Object'にし、代わりに 'synchronized(webLock){...}'を使うことができます。 – overthink

3

まず、アーキテクチャを知らなくても、WebService層に並行処理制限を適用する必要がある場合は、おそらく問題に遭遇するでしょう。従来のロックなどを使用して両方のサービス間でリクエストをシリアライズできますが、ソリューションを拡張するために2番目のWeb層を追加するとどうなりますか?ロックがWebレイヤーに対してローカルな場合、それらは無駄になります。

私はおそらく、Webサービスの下にある何らかの種類のレイヤーがあると推測しています。ここでは、これらの制限を適用する必要があります。クライアントAが競合するリクエストを行った後にクライアントBが入った場合、バックエンドは状態が変更されたことを検出すると要求を拒否し、409を第2のクライアントに返す必要があります。結局のところ、競争条件は引き続き可能ですが、最低限の共通レイヤで競合する要求から保護する必要があります。

0

何らかの種類のセマフォを使用して、一連のサービス間でアクセスを維持できます。

6

デザインに欠陥があります。サービスは冪等でなければならない。あなたが持っているクラスがそれをサポートしていない場合は、それらを再設計してください。 3つの方法のそれぞれのようなサウンドは、クラスではなくサービスの基礎でなければなりません。

+0

あなたは詳しく説明できますか? –

+0

クラスA、B、およびCのgo()メソッドが同時に実行されないようにするために私が見ることができる唯一の理由は、それらが何かを共有していることだけです。クラスデータまたはデータベースデータの場合は、何も共有しないように再設計しようとする必要があります。そうすれば、彼らは同時に動くことができ、干渉を心配する必要はありません。それが不可能な場合は、順番どおりに呼び出されないように適切な順序で呼び出すサービスを1つ用意する必要があります。どちらの場合でも、私はあなたのデザインに欠陥があると思うし、治療法は病気よりも悪くなります。もし可能ならば – duffymo

+0

+100。どこかに大きな問題があり、セマフォ、ロックなどはこれを解決する方法ではありません。 –

0

アクセスを制限するためにハイパーメディアを使用してみませんか?

使用最初のプロセスをinitateする

POST /A 

、のようなもの。それが完了したときの結果は、

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

は、第二のプロセスをinitateへのリンクをたどって、第二のプロセスを開始するために従うこと

POST /B 

をリンクを提供し、一部のために繰り返す必要C.

誤って動作しているクライアントは、手順Bへのリンクをキャッシュし、将来のリクエストでシーケンスを回避するために再利用を試みる可能性があります。ただし、ステップAを実行するときに何らかのトークンを割り当てたり、トークンをステップBおよびCに渡してクライアントがURLを手動で構築できないようにする必要はありません。

あなたのコメントをさらに読むと、AがBの前後で実行できる状況があるようです。この場合、プロセスA、B、Cの全体のステータスを表すリソースDを作成することをお勧めします。 BおよびC)。クライアントがDを取得すると、それに続くことが許されるURIが提示されます。クライアントがAプロセスを開始すると、Dリソースは処理中にBリンクを削除する必要があります。逆に、BがAの前に開始されたときに起こるはずです。

このテクニックのもう1つの利点は、状態がDで表示できるのでAまたはBが実行されていることが明らかです。実行されたDはCのリンクを含むことができます。

ハイパーメディアは、Dの同じコピーを持つ2つのクライアントを持つ可能性があり、両方ともプロセスAが実行されていないと考えられる可能性があるため、同時に実行しようとします。これは、Dの「Last Modified」タイムスタンプの何らかの種類を持つことによって対処することができ、Dの状態が変わるたびにそのタイムスタンプを更新することができます。これにより、後の要求が拒否される可能性があります。あなたのシナリオの記述に基づいて、これはもっと重要なケースであり、ハイパーメディアはプロセスを並行して実行しようとするほとんどの試みをキャッチするように見えます。

関連する問題