如何在 tomcat 7 中的 Jersey 2 中使用异步回调

Posted

技术标签:

【中文标题】如何在 tomcat 7 中的 Jersey 2 中使用异步回调【英文标题】:How to use Asynchronous Callbacks in Jersey 2 in tomcat 7 【发布时间】:2014-02-24 01:29:55 【问题描述】:

如何在tomcat服务器中使用jersey 2的异步回调。 我在 Jersey 手册中找到了一些示例:https://jersey.java.net/documentation/latest/user-guide.html#d0e8496

但是当我尝试使用以下代码测试以下代码时会出现问题:https://jersey.java.net/documentation/latest/user-guide.html#d0e8615 代码,如 Jersey 2 Manual 中所述。

------------------------------------------ -------------------------------------------------- ---------------------------------

由于手动版本已更改,我将代码发布在这里:

这是我的服务:

            import javax.ws.rs.GET; 
            import javax.ws.rs.Path; 
            import javax.ws.rs.Produces; 
            import javax.ws.rs.container.AsyncResponse; 
            import javax.ws.rs.container.CompletionCallback; 
            import javax.ws.rs.container.Suspended; 
            import javax.ws.rs.core.MediaType; 
            import javax.ws.rs.core.Response; 

            @Path("/resource") 
            public class AsyncResource  
                private static int numberOfSuccessResponses = 0; 
                private static int numberOfFailures = 0; 
                private static Throwable lastException = null; 

                @GET 
                public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse)  
                System.out.println("AsyncResource.asyncGetWithTimeout()"); 
                    asyncResponse.register(new CompletionCallback()  
                        @Override 
                        public void onComplete(Throwable throwable)  
                            if (throwable == null)  
                                // no throwable - the processing ended successfully 
                                // (response already written to the client) 
                                numberOfSuccessResponses++; 
                             else  
                                numberOfFailures++; 
                                lastException = throwable; 
                             
                         
                    ); 

                    new Thread(new Runnable()  
                        @Override 
                        public void run()  
                            String result = veryExpensiveOperation(); 
                            asyncResponse.resume(result); 
                         

                        private String veryExpensiveOperation()  
                            // ... very expensive operation 
                        return "Hi"; 
                         
                    ).start(); 
                 
            

这是客户:

    ClientConfig clientConfig = new ClientConfig(); 
    Client client = ClientBuilder.newClient(clientConfig); 

    WebTarget webTarget = client.target("http://localhost:8080/Jersey2.5Service/rest"); 
    WebTarget target = webTarget.path("resource"); 

    final AsyncInvoker asyncInvoker = target 
                    .request().async(); 
    final Future<Response> responseFuture = asyncInvoker.get(); 
    System.out.println("Request is being processed asynchronously."); 
    final Response response = responseFuture.get(); 
            // get() waits for the response to be ready 
    System.out.println("Response received." +response.readEntity(String.class));

web.xml

<servlet>  
      <servlet-name>HelloServlet</servlet-name>  
        <servlet-class>  
         org.glassfish.jersey.servlet.ServletContainer  
       </servlet-class>  
       <init-param>  
         <param-name>jersey.config.server.provider.packages</param-name>  
         <param-value>main.java</param-value>  
       </init-param>  
       <load-on-startup>1</load-on-startup>  
     </servlet>  
     <servlet-mapping>  
       <servlet-name>HelloServlet</servlet-name>  
       <url-pattern>/rest/*</url-pattern>  
     </servlet-mapping>

这些是我正在使用的罐子:

    asm-all-repackaged-2.2.0-b21.jar 
    cglib-2.2.0-b21.jar 
    guava-14.0.1.jar 
    hk2-api-2.2.0-b21.jar 
    hk2-locator-2.2.0-b21.jar 
    hk2-utils-2.2.0-b21.jar 
    javax.annotation-api-1.2.jar 
    javax.inject-2.2.0-b21.jar 
    javax.servlet-api-3.0.1.jar 
    javax.ws.rs-api-2.0.jar 
    jaxb-api-2.2.7.jar 
    jersey-client.jar 
    jersey-common.jar 
    jersey-container-servlet-core.jar 
    jersey-container-servlet.jar 
    jersey-server.jar 
    org.osgi.core-4.2.0.jar 
    osgi-resource-locator-1.0.1.jar 
    persistence-api-1.0.jar 
    servlet-api-3.0.jar 
    validation-api-1.1.0.Final.jar 

这些错误即将出现:

Jan 31, 2014 4:06:53 PM org.glassfish.jersey.servlet.internal.ResponseWriter suspend
WARNING: Attempt to put servlet request into asynchronous mode has failed. Please check your servlet configuration - all Servlet instances and Servlet filters involved in the request processing must explicitly declare support for asynchronous request processing.
java.lang.IllegalStateException: Not supported.
    at org.apache.catalina.connector.Request.startAsync(Request.java:1676)
    at org.apache.catalina.connector.RequestFacade.startAsync(RequestFacade.java:1031)
    at org.glassfish.jersey.servlet.async.AsyncContextDelegateProviderImpl$ExtensionImpl.suspend(AsyncContextDelegateProviderImpl.java:87)
    at org.glassfish.jersey.servlet.internal.ResponseWriter.suspend(ResponseWriter.java:120)
    at org.glassfish.jersey.server.ServerRuntime$AsyncResponder.suspend(ServerRuntime.java:758)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:330)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:106)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:259)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1010)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:373)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:382)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:345)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:220)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

Jan 31, 2014 4:06:53 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [HelloServlet] in context with path [/Jersey2.5Service] threw exception [javax.ws.rs.ProcessingException: Attempt to suspend a connection of an asynchronous request failed in the underlying container.] with root cause
javax.ws.rs.ProcessingException: Attempt to suspend a connection of an asynchronous request failed in the underlying container.
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:331)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:106)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:259)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:318)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1010)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:373)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:382)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:345)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:220)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

【问题讨论】:

你能发布你的servlet的源代码吗?您是否按照Jersey User Guide section 4.7.3 导入两个 Jersey 模块? @DanielDrozdzewski 发布! 您使用的是 Servlet 3.0 吗?如果有的话,你可以发布 web.xml 吗?您是否声明了任何过滤器?您正在使用的 Tomcat 的确切版本是什么? (某些 Tomcat 版本存在一些问题,例如 7.0.16、7.0.30 - tomcat.apache.org/tomcat-7.0-doc/changelog.html 您是否在 web.xml&lt;servlet&gt;...&lt;/servlet&gt; 中使用 &lt;async-supported&gt;true&lt;/async-supported&gt; @DanielDrozdzewski 不,我没有使用任何这样的标签.. 【参考方案1】:

这可能为时已晚,但升级到 tomcat 9 将解决此问题,而无需您修改服务器中的任何内容

【讨论】:

【参考方案2】:

我知道这个问题已经很老了,但是对于面临这个问题的人,请将 Tomcat 服务器更新到最新版本 8.** 。 Tomcat 从版本 8 开始支持异步处理。

【讨论】:

【参考方案3】:

对于异步回调,您可以使用 Guava lib。例如:

@GET
@Produces(MediaType.APPLICATION_JSON)
@ManagedAsync
public void getBooks(@Suspended final AsyncResponse response)

ListenableFuture<Collection<Book>> bookFuture = dao.getBooksAsync();
    Futures.addCallback(bookFuture, new FutureCallback<Collection<Book>>() 

        public void onSuccess(Collection<Book> books)
            response.resume(books);
        
        public void onFailure(Throwable thrown)
            response.resume(thrown);
        
    );

在我的 DAO 书中,我有:

BookDao()
    books = new HashMap<String,Book>();

    Book b1 = new Book();

    service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));               


Collection<Book> getBooks()

    return(books.values());


ListenableFuture<Collection<Book>> getBooksAsync()

    ListenableFuture<Collection<Book>> future = 
            service.submit(new Callable<Collection<Book>>() 
                public Collection<Book> call() throws Exception
                    return getBooks();
                
            );
    return(future);

【讨论】:

从 Java 8 开始,CompletableFutures 是内置的,其行为与 Guava 的 ListenableFuture 非常相似,未来它们可能更可取【参考方案4】:

对我来说,解决方案是在 Servlet 过滤器中添加对异步的支持。错误信息给了我一个提示:

尝试将 servlet 请求置于异步模式失败。请检查您的 servlet 配置 - 请求处理中涉及的所有 Servlet 实例和 Servlet 过滤器必须明确声明支持异步请求处理

我正在使用 Spring 进行依赖注入并使用默认的 Jersey servlet。对于 Spring,我使用委托过滤器代理。我需要声明过滤器 asycn 支持 true。

所以对于 servlet:

 <servlet>
    <servlet-name>myserlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>        
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>

并为 Spring 过滤器添加异步支持 true。

<filter>
        <description>Spring filter</description>
        <display-name>spring-filter</display-name>
        <filter-name>springFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
    </filter>

我希望这可能对你和其他人有所帮助。

【讨论】:

以上是关于如何在 tomcat 7 中的 Jersey 2 中使用异步回调的主要内容,如果未能解决你的问题,请参考以下文章

依赖注入在使用 Jersey 和 OpenWebBeans 的 Tomcat 7 上不起作用

jersey + tomcat 实现restful风格

Tomcat 7 作为 HTTP 文件服务器

如何将Swagger no config设置与Jersey 2集成

如何将 Swagger 与 Maven + Java + Jersey +Tomcat 集成

如何在 Tomcat 上的 JAX-RS (Jersey) 中返回 HTTP 404 JSON/XML 响应?