异步 JAX-RS 的用途是什么

2022-09-04 22:22:58

我正在读“RESTful Java with JAX-RS 2.0”一书。我对异步 JAX-RS 完全感到困惑,所以我在一个中提出所有问题。这本书是这样写异步服务器的:

@Path("/customers")
public class CustomerResource {

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_XML)
    public void getCustomer(@Suspended final AsyncResponse asyncResponse,
                            @Context final Request request,
                            @PathParam(value = "id") final int id) {

        new Thread() {
            @Override
            public void run() {
                asyncResponse.resume(Response.ok(new Customer(id)).build());
            }
        }.start();
    }
}

Netbeans 创建异步服务器,如下所示:

@Path("/customers")
public class CustomerResource {
    private final ExecutorService executorService = java.util.concurrent.Executors.newCachedThreadPool();

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_XML)
    public void getCustomer(@Suspended final AsyncResponse asyncResponse, 
                            @Context final Request request,
                            @PathParam(value = "id") final int id) {

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                doGetCustomer(id);
                asyncResponse.resume(javax.ws.rs.core.Response.ok().build());
            }
        });
    }

    private void doGetCustomer(@PathParam(value = "id") final int id) {
    }
}

那些不创建后台线程的那些使用一些锁定方法来存储响应对象以进行进一步处理。此示例用于向客户发送股票报价:

@Path("qoute/RHT")
public class RHTQuoteResource {

    protected List<AsyncResponse> responses;

    @GET
    @Produces("text/plain")
    public void getQuote(@Suspended AsyncResponse response) {
        synchronized (responses) {
            responses.add(response);
        }
    }
}

responses对象将与一些后台作业共享,并在准备就绪时向所有客户端发送报价。

我的问题:

  1. 在示例 1 和 2 中,Web 服务器线程(处理请求的线程)死亡,我们创建另一个后台线程。异步服务器背后的整个想法是减少空闲线程。这些示例不会减少空闲线程。一个线程死亡,另一个线程诞生。
  2. 我认为在容器内创建非托管线程是一个坏主意。我们应该只在 Java EE 7 中使用并发实用程序的托管线程。
  3. 同样,异步服务器背后的想法之一是扩展。示例 3 不缩放,不是吗?

答案 1

摘要:你太想了。


在示例 1 和 2 中,Web 服务器线程(处理请求的线程)死亡,我们创建另一个后台线程。异步服务器背后的整个想法是减少空闲线程。这些示例不会减少空闲线程。一个线程死亡,另一个线程诞生。

说实话,两者都不是特别好。在生产服务中,您不会将执行器保存在这样的私有字段中,而是将其作为单独配置的对象(例如,它自己的Spring bean)。另一方面,如果没有更多的上下文,这样一个复杂的例子对你来说将更难理解;由bean/托管资源系统组成的应用程序必须从头开始构建为这种方式。对于小规模的工作来说,对此非常小心也不是很重要,这是很多Web应用程序。

扣人心弦的是,从服务器重启中恢复实际上首先不需要担心太多。如果服务器重新启动,则可能会丢失所有连接,并且如果这些对象不是以某种方式(不能保证它们是或不是),则无法将它们存储在数据库中以启用恢复。最好不要太担心,因为你能做的不多!(如果客户没有得到任何回应,他们也会在一段时间后超时;你不能无限期地留住他们。AsyncResponseSerializable

我认为在容器内创建非托管线程是一个坏主意。我们应该只在 Java EE 7 中使用并发实用程序的托管线程。

这是一个例子!从外部提供执行器,无论您想要什么,为您的花哨的生产系统。

同样,异步服务器背后的想法之一是扩展。示例 3 不缩放,不是吗?

它只是将对象排入列表中,这根本不是一个非常慢的操作,特别是与正在进行的所有网络和反序列化/序列化的成本相比。它没有显示的是应用程序的其他部分,这些部分从该列表中删除内容,执行处理并返回结果;它们可能实施不力并导致问题,或者它们可以仔细完成并且系统运行良好。

如果你能在你的代码中做得更好,一定要这样做。(请注意,您无法将工作项存储在数据库中,或者至少您无法确定是否可以这样做,即使这碰巧是实际可行的。我对此表示怀疑。那里可能有关于TCP网络连接的信息,这从来都不容易完全存储和恢复。


答案 2

我同意你在问题1中表达的观点。让我补充一点细节,Web服务器线程不会死亡,它通常来自一个池,并为另一个Web请求释放自己。但就异步处理的效率而言,这并没有真正改变太多。在这些示例中,异步处理仅用于将处理从一个线程池传递到另一个线程池。我看不出这有什么意义。

但是有一个用例,我认为异步是有意义的,例如。当您想要注册多个客户端以等待事件并在事件发生后向所有客户端发送响应时。本文对此进行了描述:http://java.dzone.com/articles/whats-new-jax-rs-20


推荐