web-dev-qa-db-ja.com

Javaで非同期コールバックを同期的に処理する方法は?

建築関連の質問があります。これは言語に依存しない質問ですが、Javaのバックグラウンドがあるので、Javaの方法で誰かが私を案内してくれると、私にとっては簡単になります。

基本的に、私が書いているミドルウェアはSOAPベースのサードパーティサービスと通信します。呼び出しは非同期です-サービスが呼び出されると、応答01-処理で返されるように;サードパーティがリクエストを正常に受信したことを意味します。元のSOAPリクエストでは、毎回1つのコールバックURLを送信する必要があり、サードパーティが実際に結果を送信します。したがって、特定のサービスが実際に結果をすぐに返すわけではなく、結果はミドルウェアの別のHTTPエンドポイントで受信されます。

フロントエンドでは、ユーザーエクスペリエンスを複雑にしたくありません。ユーザーにミドルウェア関数を(メニュー項目/ボタンを介して)呼び出して、すぐに結果を取得してもらいたい。汚れた作業はミドルウェアに任せます。

フロントエンドから呼び出されたミドルウェア関数(X())と言います)とサードパーティが結果をプッシュするミドルウェアエンドポイントURL(Yと呼びます)は、 X()どういうわけか、待機してから、Yで取得した結果をフェッチして、その結果をフロントエンドに返す必要があります。

enter image description here

上記の動作を実現する堅牢なソリューションを構築するにはどうすればよいですか?写真は私のケースを完璧に描写しています。どんな提案も大歓迎です。

12
Tahniat Ashraf

この質問は、マルチスレッディングよりも統合パターンの方が重要かもしれません。ただし、同じアプリケーション/ JVM内のリクエストは、非同期呼び出しとオブザーバーパターンの組み合わせを使用して調整できます。

これは、例(Java=ナレッジの活用)を使用して実行することをお勧めします。シナリオを複製しようとする次の単純なコンポーネントを確認してください。

サードパーティサービス:相関IDを返し、長時間実行を開始する操作を公開します

_class ExternalService {
    public String send() {
        return UUID.randomUUID().toString();
    }
}
_

クライアント向けサービス:リクエストを受信し、サードパーティのサービスを呼び出し、結果のレシーバーに登録した後、応答のために待機

_class RequestProcessor {
    public Object submitRequest() {
        String correlationId = new ExternalService().send();

        return new ResultReceiver().register(correlationId).join();
    }
}
_

結果レシーバー:サードパーティサービスに操作を公開し、内部相関レジストリを維持します。

_class ResultReceiver {

    Map<String, CompletableFuture<Object>> subscribers;

    CompletableFuture<Object> register(String responseId) {
        CompletableFuture<Object> future = new CompletableFuture<Object>();
        this.subscribers.put(responseId, future);

        return future;
    }

    public void externalResponse(String responseId, Object result) {
        this.subscribers.get(responseId).complete(result);
    }
}
_

この場合、先物、約束、コールバックが便利です。同期は、クライアントの実行を強制的にブロックするために、初期要求プロセッサによって行われます。

これにより、この単純化されたクラスセットでは対処されない多くの問題が発生する可能性があります。これらの問題のいくつかは次のとおりです。

  • new ExternalService().send()new ResultReceiver().register(correlationId)の間の競合状態。これは、一部の応答が非常に高速である可能性があることがわからない場合(つまり、双方向の待機)、ResultReceiverで解決できるものです
  • 到来しない結果:結果には時間がかかりすぎるか、単にエラーが発生する可能性があります。これらの将来のAPIは通常、リクエストをキャンセルするためのタイムアウトを提供します。例えば:

    _new ResultReceiver().register(correlationId)
        .get(10000, TimeUnit.SECONDS);
    _
8
ernest_k

さて、それを行うことの正確な問題は何ですか?サードパーティが処理した結果を返すまで応答を返さないAPI(ミドルウェア)を作成するだけです。フロントエンドはX()にリクエストを送信しますX()リクエストをY()に送信してリクエストを処理し、ポーリングを継続しますY()は、結果が準備できたときに確認し、X()はY()から結果を取得し、それをフロントエンドのように。

制御する必要があるサードパーティのサービスの使用に関して、考慮すべき問題がいくつかあります。まず、なんらかの回路遮断器またはタイムアウトを実装する必要があります。サードパーティのサービスがハングし、結果を処理しない可能性があるため(または、処理が長すぎるため待機する意味がないため)。また、サードパーティのサービスが利用できない場合や、APIを更新したなどの理由でサイトを使用できない場合でも、サイトを継続して実行するための有意義な方法を検討する必要があります。

そして、最後に最後に一つだけ考えました。すでに非同期同期が実装されているものを作成したいのはなぜですか?時間がかかるのではないでしょうか。結果を待つためにフロントエンドを長時間ブロックすると、ユーザーエクスペリエンスが不快になり、UIが応答しなくなります。通常は、非同期リクエストに固執し、処理中のユーザーを表示しながら、その間に他のことを実行することをお勧めします。

2
Veselin Davidov