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 类中的任何时间单位来指定超时值。前面的示例没有显示将请求发送到 executor 线程池的代码。如果您只想测试超时,您可以在资源方法正文中包含对 async.SetTimeout 的调用,并且每次调用时将触发超时。

AsyncResponse.NO_TIMEOUT 值代表无限超时。

默认超时(haviour)

默认情况下,如果触发调用超时,JAX-RS 运行时会引发 ServiceUnavailableException 异常,并发回一个 HTTP 错误响应,其状态为 503

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(服务不可用)错误响应。您可以选择为 cancel 方法指定参数(即 intjava.util.Date 值),用于在响应消息中设置 Retry-After: HTTP 标头。客户端通常会忽略 Retry-After: 标头。

在可运行的实例中处理已取消响应

如果您封装了作为可运行的实例暂停的请求(在 executor 线程池中处理),您可能会发现,线程池开始处理的时间可能会取消。 因此,您很难在可运行的实例中添加一些代码,这使它能够应对取消的 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 ...
                    // ...
                }
            }
        });

    }
    ...
}