JAX-RS和长轮询

4
我试图使用JAX-RS(Jersey实现)进行长轮询,但它并不像我期望的那样工作。也许我有什么误解。我会非常感激任何建议。
请注意,出于安全原因,使用反向连接(例如Atmosphere,Comet等)不是一个选择。目前我正在使用Tomcat 7进行开发。
以下方法是从JQuery Ajax调用中调用的(使用$.ajax)。
@Path("/poll")
@GET
public void poll(@Suspended final AsyncResponse asyncResponse)
        throws InterruptedException {
    new Thread(new Runnable() {
        @Override
        public void run() {
            this.asyncResponse = asyncResponse;
            // wait max. 30 seconds using a CountDownLatch
            latch.await(getTimeout(), TimeUnit.SECONDS);
        }
    }).start();
}

另一种方法是在我的应用程序中调用(在JMS调用之后):
@POST
@Path("/printed")
public Response printCallback() {
    // ...

    // I expect the /poll call to be ended here from the client perspective but that is not the case
    asyncResponse.resume("UPDATE"); 
    latch.countDown();

    return Response.ok().build();
}

如果我在poll方法中删除线程创建,那么它可以工作,但问题是线程会一直占用。如果我使用线程创建,则该方法会直接返回,浏览器无法检测到长轮询的结束。

我做错了什么?

1个回答

4

我找到了解决问题的方法。问题在于配置上。我们需要指定Jersey servlet支持async,然后它就能正常工作了:

<servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <async-supported>true</async-supported>
    ...
</servlet>

请注意,如果您有Servlet过滤器,它们也需要将async-supported设置为true
这也不需要创建线程。Jersey会为我完成这个操作:
@Path("/poll")
@GET
public void poll(@Suspended final AsyncResponse asyncResponse)
        throws InterruptedException {
    asyncResponse.setTimeout(30, TimeUnit.SECONDS);
    this.asyncResponse = asyncResponse;
}

@POST
@Path("/printed")
public Response printCallback(String barcode) throws IOException {
    // ...

    this.asyncResponse.resume("MESSAGE");

    return Response.ok().build();
}

调用 poll 方法时,如果超时时间到达前没有收到 MESSAGE,则浏览器将等待或者接收到 HTTP 状态码 503。在服务器端,请求线程并不会被阻塞,而是直接释放。在客户端,如果发生超时,我有一个 JavaScript 方法会再次调用该方法,否则我会处理页面上的其他内容。

1
你是如何发现不需要显式创建线程的?在Jersey文档中的示例中,他们确实创建了线程:https://jersey.java.net/documentation/latest/async.html - raspacorp
1
我看到了。我认为这是示例中的问题。线程不应该由代码本身创建,否则异步请求有什么优势呢?此外,它可以在没有手动线程创建的情况下工作。如果“async-supported”设置为true,则应用服务器将创建线程。我使用调试器进行了检查。 - LaurentG
但是你的轮询方法中没有使用 '@ManagedAsync' 注解,是否遗漏了?请参考 https://jersey.java.net/apidocs/2.7/jersey/org/glassfish/jersey/server/ManagedAsync.html。 - raspacorp
或许 '@ManagedAsync' 只是一种更具体的方式(在方法级别)告诉 Jersey 必须为异步方法创建一个线程,而不是在 servlet 级别上工作的 <async-supported>true</async-supported>。 - raspacorp
你说得对。@ManagedAsync 是 Jersey 特有的。我通常更喜欢标准方式。 - LaurentG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接