48.5.2. タイムアウトおよびタイムアウトハンドラー
概要
非同期処理モデルには、REST 呼び出しにタイムアウトを指定するサポートがあります。デフォルトでは、タイムアウトになると HTTP エラー応答がクライアントに送信されます。ただし、タイムアウトハンドラーコールバックを登録するオプションもあります。これにより、タイムアウトイベントへの応答をカスタマイズできます。
ハンドラーなしでタイムアウトを設定する例
タイムアウトハンドラーを指定せずに単純な呼び出しタイムアウトを定義するには、以下の例のように AsyncResponse オブジェクトで setTimeout メソッドを呼び出します。
// Java
// Java
...
import java.util.concurrent.TimeUnit;
...
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.container.TimeoutHandler;
@Path("/bookstore")
public class BookContinuationStore {
...
@GET
@Path("/books/defaulttimeout")
public void getBookDescriptionWithTimeout(@Suspended AsyncResponse async) {
async.setTimeout(2000, TimeUnit.MILLISECONDS);
// Optionally, send request to executor queue for processing
// ...
}
...
}
java.util.concurrent.TimeUnit クラスの任意の時間単位を使用して、タイムアウト値を指定できることに注意してください。上記の例では、要求をエグゼキュータースレッドプールに送信するコードは提示されていません。タイムアウトの動作をテストするだけであれば、リソースメソッド本文に async.SetTimeout への呼び出しのみを含めると、タイムアウトは呼び出しごとにトリガーされます。
AsyncResponse.NO_TIMEOUT の値は無限のタイムアウトを表します。
デフォルトのタイムアウト動作
デフォルトでは、呼び出しタイムアウトがトリガーされると、JAX-RS ランタイムが ServiceUnavailableException 例外を発生させ、ステータス 503 で HTTP エラーの応答を返します。
TimeoutHandler インターフェイス
タイムアウトの動作をカスタマイズする場合は、TimeoutHandler インターフェイスを実装してタイムアウトハンドラーを定義する必要があります。
// Java
package javax.ws.rs.container;
public interface TimeoutHandler {
public void handleTimeout(AsyncResponse asyncResponse);
}
実装クラスで handleTimeout メソッドを上書きする場合は、タイムアウトを処理する次の方法のいずれかを選択できます。
-
asyncResponse.cancelメソッドを呼び出すことで、レスポンスを取り消します。 -
レスポンス値で
asyncResponse.resumeメソッドを呼び出すことで、レスポンスを送信します。 -
asyncResponse.setTimeoutメソッドを呼び出すことで、待機期間を延長します。たとえば、さらに 10 秒間待つには、asyncResponse.setTimeout(10, TimeUnit.SECONDS)を呼び出しできます。
ハンドラーでタイムアウトを設定する例
タイムアウトハンドラーで呼び出しタイムアウトを定義するには、以下の例のように AsyncResponse オブジェクトの setTimeout メソッドと setTimeoutHandler メソッドの両方を呼び出します。
// Java
...
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.container.TimeoutHandler;
@Path("/bookstore")
public class BookContinuationStore {
...
@GET
@Path("/books/cancel")
public void getBookDescriptionWithCancel(@PathParam("id") String id,
@Suspended AsyncResponse async) {
async.setTimeout(2000, TimeUnit.MILLISECONDS);
async.setTimeoutHandler(new CancelTimeoutHandlerImpl());
// Optionally, send request to executor queue for processing
// ...
}
...
}
この例では、呼び出しタイムアウトを処理するために CancelTimeoutHandlerImpl タイムアウトハンドラーのインスタンスを登録します。
タイムアウトハンドラーを使用した応答の取り消し
CancelTimeoutHandlerImpl タイムアウトハンドラーは以下のように定義されます。
// Java
...
import javax.ws.rs.container.AsyncResponse;
...
import javax.ws.rs.container.TimeoutHandler;
@Path("/bookstore")
public class BookContinuationStore {
...
private class CancelTimeoutHandlerImpl implements TimeoutHandler {
@Override
public void handleTimeout(AsyncResponse asyncResponse) {
asyncResponse.cancel();
}
}
...
}
AsyncResponse オブジェクト上で cancel を呼び出す効果は、クライアントに HTTP 503 (Service unavailable) エラーの応答を送信することです。任意で、cancel メソッド (int または java.util.Date の値) の引数を指定できます。これは応答メッセージで Retry-After: HTTP ヘッダーを設定するために使用されます。ただし、クライアントは多くの場合で Retry-After: ヘッダーを無視します。
Runnable インスタンスでの取り消し済みの応答への対応
エグゼキュータースレッドプールで処理のためにキューに格納された Runnable インスタンスとして、一時停止されたリクエストをカプセル化した場合、スレッドプールがリクエストを処理するまでに AsyncResponse がキャンセルされる可能性があります。このため、Runnable インスタンスにコードを追加する必要があります。これにより、キャンセルされた AsyncResponse オブジェクトに対応できるようになります。以下に例を示します。
// Java
...
@Path("/bookstore")
public class BookContinuationStore {
...
private void sendRequestToThreadPool(final String id, final AsyncResponse response) {
executor.execute(new Runnable() {
public void run() {
if ( !response.isCancelled() ) {
// Process the suspended request ...
// ...
}
}
});
}
...
}