如何正确中断 JAX-RS AJAX 请求

Posted

技术标签:

【中文标题】如何正确中断 JAX-RS AJAX 请求【英文标题】:How to properly interrupt JAX-RS AJAX request 【发布时间】:2012-12-29 13:45:03 【问题描述】:

我有一个 Java EE 应用程序部署到 JBoss 7.1.1.Final。该应用程序利用 JAX-RS 向客户端提供 REST 服务。其中一项服务保持客户端连接,直到通过BlockingQueue 接收到响应对象。到目前为止,这似乎工作正常。但是,即使删除了应用程序部署/应用程序关闭,连接也会保持打开状态。它甚至可以防止 JBoss 关闭。

所以我试图通过@PreDestroy 回调中断等待线程来执行干净关闭。服务如下所示:

@Path("/mypath")
@SessionScoped
public class MyResource implements Serializable 
  private static final long serialVersionUID = 1L;

  @Inject
  private EntityManager em;

  private Thread thread = null;

  @GET
  @Path("/id:[0-9][0-9]*")
  @Produces(
    MediaType.TEXT_PLAIN,
    MediaType.TEXT_XML
  )
  public Response getObjects(@PathParam("id") long id) 
    MyGroup group = this.em.find(MyGroup.class, id);
    if (group == null) 
      return Response.status(Status.NOT_FOUND)
          .entity("Group not found\n")
          .build();
    

    if (group.isEmpty()) 
      return Response.status(Status.PRECONDITION_FAILED)
          .entity("Group is empty\n")
          .build();
    

    BlockingQueue<?> queue = ... // Get queue for group

    this.thread = Thread.currentThread();
    try 
      MyObject object = queue.take();
     catch (InterruptedException e) 
      return Response.noContent().build();
    

    return Response.ok(object)
        .type(MediaType.TEXT_XML)
        .build();
  

  @PreDestroy
  public void shutdown() 
    if (this.thread != null) 
      this.thread.interrupt();
    
  

它似乎可以工作,有点...客户端收到预期的响应。但是,JBoss 会打印此异常:

13:38:44,489 ERROR [org.jboss.as.txn] JBAS010151: Unable to get transaction state: java.lang.IllegalStateException
    at org.jboss.msc.value.InjectedValue.getValue(InjectedValue.java:47)
    at org.jboss.as.txn.deployment.TransactionRollbackSetupAction.teardown(TransactionRollbackSetupAction.java:43)
    at org.jboss.as.web.ThreadSetupBindingListener.unbind(ThreadSetupBindingListener.java:61) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:195) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Thread.java:636) [rt.jar:1.6.0_18]

13:38:44,492 ERROR [org.apache.catalina.connector.CoyoteAdapter] An exception or error occurred in the container during the request processing: java.lang.RuntimeException: java.lang.IllegalStateException
    at org.jboss.as.web.ThreadSetupBindingListener.unbind(ThreadSetupBindingListener.java:67) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:195) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
    at java.lang.Thread.run(Thread.java:636) [rt.jar:1.6.0_18]
Caused by: java.lang.IllegalStateException
    at org.jboss.msc.value.InjectedValue.getValue(InjectedValue.java:47)
    at org.jboss.as.connector.deployers.processors.CachedConnectionManagerSetupProcessor$CachedConnectionManagerSetupAction.teardown(CachedConnectionManagerSetupProcessor.java:83)
    at org.jboss.as.web.ThreadSetupBindingListener.unbind(ThreadSetupBindingListener.java:61) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
    ... 8 more

这感觉不对。中断此类等待请求的正确方法是什么?

【问题讨论】:

是否有理由不能立即使用回调令牌向客户端返回 202 状态?那会更加RESTful。在任何情况下,您可以立即做出的一项更改是在循环中将您的接听电话更改为超时轮询。 将请求视为事件的实时提要。应用程序无法确定此类事件是否以及何时到达/发生。立即 202 响应只会产生负载而没有任何好处,对吧?另外,我看不出循环中的超时轮询如何有助于改善这种情况 - 请解释一下。 为您的用例使用 Comet 框架可能会更好。原因是您的 Web 应用程序服务器使用线程池来为传入请求提供服务,并且您在(可以想象的)相当长的时间内保持线程。 我遇到了同样的问题。我认为您的解决方案还可以(除非它真的应该是 @RequestScoped 而不是 @SessionsScoped 【参考方案1】:

您正在中断由容器创建的线程,因此容器无法完成线程本身。您可以使用状态变量来检查线程是否应该结束,而不是中断,例如(将其解释为伪代码):

while( ! stopped ) 
    MyObject object = queue.poll(25, TimeUnit.MILLISECONDS);

    return Response.ok(object)
        .type(MediaType.TEXT_XML)
        .build();


return Response.noContent().build();

并在@PreDestroy 中将stopped 设置为true。还有AsyncResponse 功能。您可以将所有打开的请求存储在列表中,然后通过@PreDestroy 取消它们。

【讨论】:

以上是关于如何正确中断 JAX-RS AJAX 请求的主要内容,如果未能解决你的问题,请参考以下文章

使用 JAX-RS/RESTEasy 实现 CORS 的 Ajax 请求

如何在 JAX-RS 客户端中记录请求正文

如何中断 Rails 中的 AJAX 请求?

关于js中断ajax请求

带有 Resteasy 的 JAX-RS:如何进行“超类”请求标头管理? [复制]

需要身份验证时,带有 JSON 中断的跨源 AJAX 请求