使用 Jersey 在运行时决定同步或异步响应

Decide at runtime for sync or async response using Jersey

是否可以在运行时决定对资源端点的 Jersey REST 请求是同步处理还是异步处理?举个简单的例子。

同步版本:

@Path("resource")
public class Resource {
    @GET
    @Produces({MediaType.TEXT_PLAIN})
    public Response get() {
        return Response.ok("Hello there!").build();
    }
}

异步版本:

@Path("resource")
public class Resource {
    @GET
    @Produces({MediaType.TEXT_PLAIN})
    public void get(@Suspended final AsyncResponse r) {
        r.resume(Response.ok("Hello there!").build()); // usually called somewhere from another thread
    }
}

根据某些参数,我想在运行时决定是同步还是异步处理 GET 请求。在这两种情况下,资源端点 (http://server/resource) 的 URL 必须相同。这可能吗?

当然,正如您在上面的示例中看到的,可以通过简单地调用 AsyncResponse.resume(...) 以异步方式伪造同步版本。但是,我会避免创建异步响应的开销。

您可以使用自定义 MediaType...例如,您可以将 @Produces("simple") 放在简单的 get 方法上,将 @Produces("asynch") 放在异步 get 方法上。然后,在您的客户端中,您可以根据需要将呼叫的接受 Header 设置为 "simple" 或 "asynch"。

退一步

JAX-RS异步服务器API是关于容器如何管理请求的。但它仍会保留请求,不会影响客户端体验

引用关于 Asynchronous Server API 的 Jersey 文档:

Note that the use of server-side asynchronous processing model will not improve the request processing time perceived by the client. It will however increase the throughput of the server, by releasing the initial request processing thread back to the I/O container while the request may still be waiting in a queue for processing or the processing may still be running on another dedicated thread. The released I/O container thread can be used to accept and process new incoming request connections.

下面描述的方法不会给您的客户带来任何好处。

使用自定义 header

您可以为同步和异步方法设置不同的 URL,并创建一个 pre-matching filter,它在请求匹配开始之前执行。

为此,实施 ContainerRequestFilter, annotate it with @PreMatching 并根据您的条件(headers、参数等)更改请求的 URI:

@Provider
@PreMatching
public class PreMatchingFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        if (requestContext.getHeaders().get("X-Use-Async") != null) {
            requestContext.setRequestUri(yourNewURI);
        }
    }
}

看看ContainerRequestContext API

使用自定义媒体类型

我还没有测试过以下解决方案,但它应该有效。您可以为同步和异步方法保持相同的 URL,只是为每种方法接受不同的内容类型。

例如:

  • 同步方法:@Consumes("application/vnd.example.sync+text")
  • 异步方法:@Consumes("application/vnd.example.async+text")

并根据您的条件使用 PreMatchingFilter 更改 Content-Type header,如下所示:

if (useSync) {
    requestContext.getHeaders().putSingle(
        HttpHeaders.CONTENT_TYPE, "application/vnd.example.sync+text");
} else {
    requestContext.getHeaders().putSingle(
        HttpHeaders.CONTENT_TYPE, "application/vnd.example.async+text");
}

根据 documentation, ContainerRequestContext#getHeaders() returns 带有请求的可变映射 headers.