1

MusicbrainzのWebサービスにリクエストを送信するアプリを開発しています。musicbrainzのマニュアルを読んで、Webサービスに対して1秒間に複数のリクエストを行わないようにします。そうしないとクライアントIPがブロックされます。

この制限をサービスクライアントに対して透過的にするために、どのアーキテクチャを提案しますか。

  • メソッド(たとえばgetAlbuns)を呼び出したいのですが、最後のリクエストから1秒後にのみリクエストを行う必要があります。
  • また、一度に10個のリクエストを呼び出したいのですが、サービスはキューイングを処理し、利用可能な場合は結果を返します(非ブロッキング)。

ありがとう!

4

2 に答える 2

1

ローカルクライアントが呼び出すローカルの「プロキシサービス」を定義する必要があります。

ローカルプロキシはリクエストを受信し、それを実際のサービスに渡します。ただし、1秒あたり1メッセージの速度でのみです。

これをどのように行うかは、利用可能な技術に大きく依存します。

最も単純なのは、静的で同期されたLastRequestTime long; "タイムスタンプ変数を使用するマルチスレッドJavaサービスです(ただし、要求を順番に保持するには、コードの曲芸が必要になります)。

より洗練されたサービスでは、ワーカースレッドがリクエストを受信して​​キューに配置し、単一のスレッドがリクエストを取得して実際のサービスに渡すことができます。

于 2009-04-24T01:55:53.170 に答える
1

java.util.Timer呼び出しの間に必要な遅延があるため、またはをお勧めしjava.util.concurrent.ScheduledThreadPoolExecutorます。は非常にシンプルで、このユースケースTimerには完全に適しています。ただし、後で追加のスケジューリング要件が特定された場合は、1人ですべてを処理できます。いずれの場合も、固定レート方式ではなく、固定遅延方式を使用してください。Executor

繰り返しタスクは、要求オブジェクトの並行キューをポーリングします。保留中のリクエストがある場合、タスクはそれを実行し、コールバックを介して結果を返します。サービスのクエリと呼び出すコールバックは、リクエストオブジェクトのメンバーです。

アプリケーションは共有キューへの参照を保持します。リクエストをスケジュールするには、リクエストをキューに追加するだけです。


明確にするために、スケジュールされたタスクの実行時にキューが空の場合、要求は行われません。単純なアプローチは、タスクを終了することであり、スケジューラーは1秒後にタスクを呼び出して再度チェックします。

ただし、これは、最近処理された要求がない場合でも、タスクの開始に最大1秒かかる可能性があることを意味します。この不必要な待ち時間が耐えられない場合は、Timerまたはを使用するよりも、独自のスレッドを作成することをお勧めしますScheduledThreadPoolExecutor。独自のタイミングループでは、リクエストが利用可能になるまで空のキューでブロックすることを選択した場合、スケジューリングをより細かく制御できます。組み込みのタイマーは、前回の実行が終了してから1秒待つことは保証されていません。それらは通常、タスクの開始時間に関連してスケジュールされます。

この2番目のケースがあなたが考えているものである場合、run()メソッドにはループが含まれます。各反復は、要求が受信されるまでキューをブロックし、時間を記録することから始まります。リクエストを処理した後、時刻が再度チェックされます。時間差が1秒未満の場合は、残りの時間はスリープします。この設定では、あるリクエストの開始から次のリクエストの開始までに1秒の遅延が必要であると想定しています。あるリクエストの終了から次のリクエストまでの間に遅延が必要な場合は、時間を確認する必要はありません。1秒間寝るだけです。

もう1つ注意すべき点は、サービスが1つのリクエストで複数のクエリを受け入れることができる可能性があることです。これによりオーバーヘッドが削減されます。含まれている場合はtake()、最初の要素をブロックしてpoll()から、おそらく非常に短いブロック時間(5ミリ秒程度)でを使用して、アプリケーションがそれ以上要求を行っているかどうかを確認することで、これを利用します。その場合、これらはサービスへの単一のリクエストにまとめることができます。の場合queue、次のBlockingQueue<? extends Request>ようになります。

    Collection<Request> bundle = new ArrayList<Request>();
    bundle.add(queue.take());
    while (bundle.size() < BUNDLE_MAX) {
      Request req = queue.poll(EXTRA, TimeUnit.MILLISECONDS);
      if (req == null)
        break;
      bundle.add(req);
    }
    /* Now make one service request with contents of "bundle". */
于 2009-04-24T02:01:40.783 に答える