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 ... // ... } } }); } ... }