Springサービスで長期実行タスクを定義しました。これは、Spring MVCコントローラーによって開始されます。サービスを開始し、サービスが終了する前にHttpResponse
を呼び出し元に返したい。サービスは最後にファイルシステムにファイルを保存します。 JavaScriptで、サービスステータスをチェックするためのポーリングジョブを作成しました。
Spring 3.2で@Async
アノテーションを見つけましたが、DeferredResult
やCallable
との違いがわかりません。いつ@Async
を使用する必要があり、いつDeferredResult
を使用する必要がありますか?
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と多少同等です。
コントローラは、最終的にはサーブレットコンテナ(Tomcatであると想定)のワーカースレッドによって実行される関数です。サービスフローはTomcatで始まり、Tomcatで終わります。 Tomcatはクライアントから要求を取得し、接続を保持して、最終的にクライアントに応答を返します。コード(コントローラーまたはサーブレット)が途中にあります。
このフローを考えてみましょう:
サーブレット(コード)とサーブレットコンテナ(Tomcat)は異なるエンティティであるため、このフローを許可するには(Tomcatスレッドを解放するがクライアント接続は維持する)、 theirでこのサポートが必要です。 contract 、パッケージjavax.servlet
、これは Servlet 3.0 で導入されました。さて、あなたの質問に戻りますが、Spring MVCは、コントローラーの戻り値がDeferredResult
またはCallable
であるときに新しいサーブレット3.0機能を使用しますが、これらは2つの異なるものです。 Callable
はJava.util
の一部であるインターフェースであり、Runnable
インターフェースの改良版です(以下で実装する必要があります)インスタンスがスレッドによって実行されることが意図されているクラス)。 Callable
では値を返すことができますが、Runnable
ではできません。 DeferredResult
は、Springによって設計されたclassであり、Spring MVCでの非同期リクエスト処理のオプション(後で説明)を増やすことができ、このクラスは結果(その名前が示すとおり)Callable
実装は非同期コードを保持します。つまり、両方をコントローラーで使用し、Callable
で非同期コードを実行し、コントローラーの戻り値となるDeferredResult
に結果を設定できることを意味します。では、Callableの代わりにDeferredResult
を戻り値として使用すると何が得られますか? DeferredResult
には、onError
、onTimeout
、onCompletion
などの組み込みのコールバックがあります。エラー処理が非常に簡単になります。さらに、これは単なる結果コンテナーであるため、非同期コードで実行するスレッド(またはスレッドプール)を選択できます。 Callableでは、この選択肢はありません。
@Async
に関しては、はるかに単純です。Beanのメソッドに@Async
でアノテーションを付けると、別のスレッドで実行されます。デフォルトでは(オーバーライド可能)、SpringはSimpleAsyncTaskExecutor
を使用してこれらのメソッドを非同期で実際に実行します。
結論として、Tomcatスレッドを解放し、重い処理を行っている間クライアントとの接続を維持したい場合、コントローラーはCallable
またはDeferredResult
を返す必要があります。それ以外の場合は、@Async
で注釈が付けられたメソッドでコードを実行できます。
DeferredResult
は、サーブレット3.0 AsyncContextを利用します。結果を返す必要がある場合、他のスレッドのようにスレッドをブロックしません。
もう1つの大きな利点は、DeferredResult
がコールバックをサポートすることです。