我正在尝试在Java Web应用程序中实现长轮询。该应用程序是使用Spring MVC 3.2编写的,我使用DeferredResult对象(它表示Servlet 3.0异步响应功能)。
问题是,使用DeferredResult时,每次发送请求10秒钟后,我都会收到内部服务器错误(代码500)。预期结果是30秒后的文本数据。我的应用程序日志中没有任何内容,Tomcat日志中没有任何内容,但是在FireFox网络监视器中没有任何内容,我看到此500错误。
另一方面,当我发送同步请求并在服务器中保留30秒钟时,它成功完成。
谁能帮我这个?
这篇文章的末尾有一个测试应用程序下载链接。这是一个Maven应用程序。
这是我的应用代码:
ResponseController.java
@Controller @RequestMapping("responses") public class ResponseController { @Autowired private ResponseService messagesService; @RequestMapping(value="/async", method=RequestMethod.GET) @ResponseBody public DeferredResult<String> getAsyncUpdate() { return messagesService.getAsyncUpdate(); } @RequestMapping(value="/sync", method=RequestMethod.GET) @ResponseBody public String getSyncUpdate() { return messagesService.getSyncUpdate(); } }
ResponseService.java
@Service public class ResponseService { private DeferredResult<String> deferredResult; public DeferredResult<String> getAsyncUpdate(){ deferredResult = new DeferredResult<String>(); return deferredResult; } public String getSyncUpdate(){ long startTime = System.currentTimeMillis(); while( System.currentTimeMillis() - startTime <30000){ } return "RESULT"; } @Scheduled(fixedDelay=500) public void refresh(){ if(deferredResult != null){ long startTime = System.currentTimeMillis(); while( System.currentTimeMillis() - startTime <30000){ } deferredResult.setResult("RESULT"); } } }
mvc-dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <context:component-scan base-package="pl.prv.mra.test.app" /> <context:annotation-config/> <aop:aspectj-autoproxy/> <mvc:annotation-driven > <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /> </mvc:message-converters> </mvc:annotation-driven> <task:annotation-driven/> <mvc:resources location="/resources/**" mapping="/resources/**"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
jQuery请求异步响应
$.ajax({ url : "responses/async", type : "GET", timeout : 60000, dataType : "json", success : function(){ alert('SUCCESS'); }, error : function(){ alert('FAILURE'); } });
jQuery请求同步响应
$.ajax({ url : "responses/sync", type : "GET", timeout : 60000, dataType : "json", success : function(){ alert('SUCCESS'); }, error : function(){ alert('FAILURE'); } });
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>test-app</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
Tomcat server.xml文件
<?xml version='1.0' encoding='utf-8'?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" /> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine> </Service> </Server>
我的环境配置
操作系统:Linux Mint 16
伺服器:Apache Tomcat 7.0.55
Java:Java 1.7.0_67
测试应用程序下载链接
更新1
这是一个响应数据:
响应头:
连接:“关闭”
内容长度:“ 0”
日期:“ 2014年10月27日星期一19:48:10 GMT”
服务器:“ Apache-Coyote / 1.1”
更新2
这是我的应用程序日志。我已将日志记录级别配置为ALL。不幸的是,它们根本没有错误。(我对tomcat日志执行了相同的操作,但结果相同):
2014-11-01 15:33:09 DEBUG RequestMappingHandlerMapping:220 - Looking up handler method for path /responses/async 2014-11-01 15:33:09 TRACE RequestMappingHandlerMapping:264 - Found 1 matching mapping(s) for [/responses/async] : [{[/responses/async],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}] 2014-11-01 15:33:09 DEBUG RequestMappingHandlerMapping:227 - Returning handler method [public org.springframework.web.context.request.async.DeferredResult<java.lang.String> pl.prv.mra.test.app.controllers.ResponseController.getAsyncUpdate()] 2014-11-01 15:33:09 DEBUG DefaultListableBeanFactory:246 - Returning cached instance of singleton bean 'responseController' 2014-11-01 15:33:09 TRACE DispatcherServlet:1122 - Testing handler adapter [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@7e036b3] 2014-11-01 15:33:09 DEBUG DispatcherServlet:912 - Last-Modified value for [/test-app/responses/async] is: -1 2014-11-01 15:33:09 TRACE HandlerMethod:129 - Invoking [getAsyncUpdate] method with arguments [] 2014-11-01 15:33:09 TRACE HandlerMethod:135 - Method [getAsyncUpdate] returned [org.springframework.web.context.request.async.DeferredResult@4810881c] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@2a426218] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.method.annotation.ModelMethodProcessor@6c362b54] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@5856cf4c] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@2b43b3e3] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@deae877] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@3943e3c4] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>] 2014-11-01 15:33:10 DEBUG WebAsyncManager:429 - Concurrent handling starting for GET [/test-app/responses/async] 2014-11-01 15:33:10 TRACE DispatcherServlet:1028 - Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@655d2842 2014-11-01 15:33:10 DEBUG DispatcherServlet:963 - Leaving response open for concurrent processing 2014-11-01 15:33:10 TRACE XmlWebApplicationContext:332 - Publishing event in WebApplicationContext for namespace 'mvc-dispatcher-servlet': ServletRequestHandledEvent: url=[/test-app/responses/async]; client=[127.0.0.1]; method=[GET]; servlet=[mvc-dispatcher]; session=[55143812B6586F66C0001D3F41B1947C]; user=[null]; time=[13ms]; status=[OK] 2014-11-01 15:33:10 TRACE XmlWebApplicationContext:332 - Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/test-app/responses/async]; client=[127.0.0.1]; method=[GET]; servlet=[mvc-dispatcher]; session=[55143812B6586F66C0001D3F41B1947C]; user=[null]; time=[13ms]; status=[OK]
好。我找到了解决方案。
在Tomcat配置中(在server.xml文件中),有一个HTTP / 1.1连接器。我要强调的是connectionTimeout参数定义了每个连接的超时,但是没有定义。
对于异步连接(例如我的情况),有一个单独的参数称为asyncTimeout。默认值为10秒。
asyncTimeout
像这样设置:
<Connector port="8080" protocol="HTTP/1.1" asyncTimeout="60000" connectionTimeout="20000" redirectPort="8443" />
解决了我的问题。