web-dev-qa-db-ja.com

Spring MVCの@ Async、DeferredResult、Callableの違い

Springサービスで長期実行タスクを定義しました。これは、Spring MVCコントローラーによって開始されます。サービスを開始し、サービスが終了する前にHttpResponseを呼び出し元に返したい。サービスは最後にファイルシステムにファイルを保存します。 JavaScriptで、サービスステータスをチェックするためのポーリングジョブを作成しました。

Spring 3.2で@Asyncアノテーションを見つけましたが、DeferredResultCallableとの違いがわかりません。いつ@Asyncを使用する必要があり、いつDeferredResultを使用する必要がありますか?

26
Premier

Asyncはメソッドに注釈を付けるため、非同期で呼び出されます。

@org.springframework.stereotype.Service
public class MyService {
    @org.springframework.scheduling.annotation.Async
    void DoSomeWork(String url) {
        [...]
    }
}

したがって、Springが実行できるので、実行方法を定義する必要があります。例えば:

<task:annotation-driven />
<task:executor id="executor" pool-size="5-10" queue-capacity="100"/>

この方法でservice.DoSomeWork( "parameter")を呼び出すと、その呼び出しはエグゼキューターのキューに入れられ、非同期に呼び出されます。これは、同時に実行できるタスクに役立ちます。

Asyncを使用して、あらゆる種類の非同期タスクを実行できます。定期的にタスクを呼び出す場合は、@ Scheduledを使用できます(およびtask:executorの代わりにtask:schedulerを使用します)。これらは、Java Runnablesを呼び出す簡単な方法です。

DeferredResult <>は、応答に使用されるTomcat HTTPスレッドをブロックせずに請願に応答するために使用されます。通常は、ResponseBodyアノテーション付きメソッドの戻り値になります。

@org.springframework.stereotype.Controller
{
    private final Java.util.concurrent.LinkedBlockingQueue<DeferredResult<String>> suspendedRequests = new Java.util.concurrent.LinkedBlockingQueue<>();

    @RequestMapping(value = "/getValue")
    @ResponseBody
    DeferredResult<String> getValue() {
            final DeferredResult<String> result = new DeferredResult<>(null, null);
            this.suspendedRequests.add(result);
            result.onCompletion(new Runnable() {
            @Override
            public void run() {
        suspendedRequests.remove(result);
            }
});
            service.setValue(result); // Sets the value!
            return result;
    }
}

前の例には1つの重要な点が欠けており、延期された結果がどのように設定されるかを示していません。他のいくつかのメソッド(おそらくsetValueメソッド)では、result.setResult(value)が存在します。 setResult Springの呼び出し後、onCompletionプロシージャが呼び出され、HTTPリクエストに応答が返されます( https://en.wikipedia.org/wiki/Push_technology#Long_polling を参照)。

ただし、setValueを同期的に実行しているだけの場合は、遅延結果を使用する利点はありません。ここで、Asyncが役に立ちます。非同期メソッドを使用して、別のスレッドを使用して将来のある時点で戻り値を設定できます。

    @org.springframework.scheduling.annotation.Async
    void SetValue(DeferredResult<String> result) {
        String value;
        // Do some time consuming actions
        [...]
        result.setResult(value);
    }

非同期は、遅延結果を使用するために必要ではなく、それを実行する1つの方法にすぎません。

この例では、延期された結果のキューがあり、たとえば、スケジュールされたタスクが保留中の要求を処理するために監視している可能性があります。また、いくつかの非ブロッキングメカニズム( http://en.wikipedia.org/wiki/New_I/O を参照)を使用して戻り値を設定することもできます。

画像を完成させるには、Java標準先物( http://docs.Oracle.com/javase/1.5.0/docs/api/Java/util/ concurrent/Future.html )と呼び出し可能オブジェクト( http://docs.Oracle.com/javase/1.5.0/docs/api/Java/util/concurrent/Callable.html )that Spring DeferredResultおよびAsyncと多少同等です。

21
Josep Panadero

コントローラは、最終的にはサーブレットコンテナ(Tomcatであると想定)のワーカースレッドによって実行される関数です。サービスフローはTomcatで始まり、Tomcatで終わります。 Tomcatはクライアントから要求を取得し、接続を保持して、最終的にクライアントに応答を返します。コード(コントローラーまたはサーブレット)が途中にあります。

このフローを考えてみましょう:

  1. Tomcatはクライアント要求を取得します。
  2. Tomcatがコントローラーを実行します。
  3. Tomcatスレッドを解放しますが、クライアント接続を維持し(応答を返さないでください)、別のスレッドで重い処理を実行します。
  4. 重い処理が完了したら、Tomcatをその応答で更新し、クライアントに返します(Tomcatによって)。

サーブレット(コード)とサーブレットコンテナ(Tomcat)は異なるエンティティであるため、このフローを許可するには(Tomcatスレッドを解放するがクライアント接続は維持する)、 theirでこのサポートが必要です。 contract 、パッケージjavax.servlet、これは Servlet 3.0 で導入されました。さて、あなたの質問に戻りますが、Spring MVCは、コントローラーの戻り値がDeferredResultまたはCallableであるときに新しいサーブレット3.0機能を使用しますが、これらは2つの異なるものです。 CallableJava.utilの一部であるインターフェースであり、Runnableインターフェースの改良版です(以下で実装する必要があります)インスタンスがスレッドによって実行されることが意図されているクラス)。 Callableでは値を返すことができますが、Runnableではできません。 DeferredResultは、Springによって設計されたclassであり、Spring MVCでの非同期リクエスト処理のオプション(後で説明)を増やすことができ、このクラスは結果(その名前が示すとおり)Callable実装は非同期コードを保持します。つまり、両方をコントローラーで使用し、Callableで非同期コードを実行し、コントローラーの戻り値となるDeferredResultに結果を設定できることを意味します。では、Callableの代わりにDeferredResultを戻り値として使用すると何が得られますか? DeferredResultには、onErroronTimeoutonCompletionなどの組み込みのコールバックがあります。エラー処理が非常に簡単になります。さらに、これは単なる結果コンテナーであるため、非同期コードで実行するスレッド(またはスレッドプール)を選択できます。 Callableでは、この選択肢はありません。

@Async に関しては、はるかに単純です。Beanのメソッドに@Asyncでアノテーションを付けると、別のスレッドで実行されます。デフォルトでは(オーバーライド可能)、SpringはSimpleAsyncTaskExecutorを使用してこれらのメソッドを非同期で実際に実行します。

結論として、Tomcatスレッドを解放し、重い処理を行っている間クライアントとの接続を維持したい場合、コントローラーはCallableまたはDeferredResultを返す必要があります。それ以外の場合は、@Asyncで注釈が付けられたメソッドでコードを実行できます。

6
KernelMode

DeferredResultは、サーブレット3.0 AsyncContextを利用します。結果を返す必要がある場合、他のスレッドのようにスレッドをブロックしません。

もう1つの大きな利点は、DeferredResultがコールバックをサポートすることです。

3
Bart