严重:GET 的异步超时 [使用 Spring MVC 4.3.x、Java 8、Tomcat 7 流式传输大量数据]
SEVERE: Async timeout for GET [ Streaming huge data Using Spring MVC 4.3.x, Java 8, Tomcat 7 ]
我正在使用 Spring MVC 4.3.9.RELEASE, Java 8, Tomcat 7
我的代码如下,
@Controller
@EnableWebMvc
public class StreamRecordsController {
@RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET,
produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<StreamingResponseBody> streamRecords(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable(value = "elementname", required = true) String elementName,
@RequestParam(value = "customerid", required = true) long customerId,
@RequestParam(value = "userid", required = true) long userId) throws Exception {
StreamingResponseBody responseBody = outputStream -> {
/**
* 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
* 2. Write json to outputStream.
*/
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
}
}
我收到如下错误,
May 10, 2019 11:45:41 AM org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver handleAsyncRequestTimeoutException
SEVERE: Async timeout for GET [/server/streamrecords/xyz]
May 10, 2019 11:46:01 AM org.apache.catalina.connector.CoyoteAdapter checkRecycled
INFO: Encountered a non-recycled response and recycled it forcedly.
org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException
at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:634)
at org.apache.coyote.http11.AbstractHttp11Processor.recycle(AbstractHttp11Processor.java:1909)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.release(Http11AprProtocol.java:245)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:720)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2574)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2563)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
org.apache.catalina.connector.ClientAbortException: java.io.IOException
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:370)
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:334)
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101)
at com.entrib.emg.server.rest.api.services.ElementService.lambda$streamRecords(ElementService.java:170)
at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:106)
at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:93)
at org.springframework.web.context.request.async.WebAsyncManager.run(WebAsyncManager.java:316)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException
at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:205)
at org.apache.coyote.http11.InternalAprOutputBuffer.flush(InternalAprOutputBuffer.java:109)
at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:850)
at org.apache.coyote.Response.action(Response.java:171)
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366)
... 9 more
问题:
我错过了什么?如何让我的异步请求不超时并等待输出流正常关闭?
我唯一要做的就是扩展 org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
并覆盖它的方法 configureAsyncSupport(AsyncSupportConfigurer configurer)
。您可以使用以下代码设置超时。
@Controller
@EnableWebMvc
public class StreamRecordsController extends WebMvcConfigurerAdapter{
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(100000000); //in milliseconds (20 hours)
super.configureAsyncSupport(configurer);
}
@RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET,
produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<StreamingResponseBody> streamRecords(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable(value = "elementname", required = true) String elementName,
@RequestParam(value = "customerid", required = true) long customerId,
@RequestParam(value = "userid", required = true) long userId) throws Exception {
StreamingResponseBody responseBody = outputStream -> {
/**
* 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
* 2. Write json to outputStream.
*/
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
}
}
由于 WebMvcConfigurerAdapter
现已弃用,class 现在必须实施 WebMvcConfigurer
并且 Ankur 的答案已成功重写为:
@RestController
@RequestMapping("/api")
@Log4j2
public class StreamRecordsController implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(2_000_000);
}
...
使用 Spring Boot 2.4 进行测试。
我正在使用 Spring MVC 4.3.9.RELEASE, Java 8, Tomcat 7
我的代码如下,
@Controller
@EnableWebMvc
public class StreamRecordsController {
@RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET,
produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<StreamingResponseBody> streamRecords(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable(value = "elementname", required = true) String elementName,
@RequestParam(value = "customerid", required = true) long customerId,
@RequestParam(value = "userid", required = true) long userId) throws Exception {
StreamingResponseBody responseBody = outputStream -> {
/**
* 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
* 2. Write json to outputStream.
*/
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
}
}
我收到如下错误,
May 10, 2019 11:45:41 AM org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver handleAsyncRequestTimeoutException
SEVERE: Async timeout for GET [/server/streamrecords/xyz]
May 10, 2019 11:46:01 AM org.apache.catalina.connector.CoyoteAdapter checkRecycled
INFO: Encountered a non-recycled response and recycled it forcedly.
org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException
at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:634)
at org.apache.coyote.http11.AbstractHttp11Processor.recycle(AbstractHttp11Processor.java:1909)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.release(Http11AprProtocol.java:245)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:720)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2574)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2563)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
org.apache.catalina.connector.ClientAbortException: java.io.IOException
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:370)
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:334)
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101)
at com.entrib.emg.server.rest.api.services.ElementService.lambda$streamRecords(ElementService.java:170)
at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:106)
at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:93)
at org.springframework.web.context.request.async.WebAsyncManager.run(WebAsyncManager.java:316)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException
at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:205)
at org.apache.coyote.http11.InternalAprOutputBuffer.flush(InternalAprOutputBuffer.java:109)
at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:850)
at org.apache.coyote.Response.action(Response.java:171)
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366)
... 9 more
问题:
我错过了什么?如何让我的异步请求不超时并等待输出流正常关闭?
我唯一要做的就是扩展 org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
并覆盖它的方法 configureAsyncSupport(AsyncSupportConfigurer configurer)
。您可以使用以下代码设置超时。
@Controller
@EnableWebMvc
public class StreamRecordsController extends WebMvcConfigurerAdapter{
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(100000000); //in milliseconds (20 hours)
super.configureAsyncSupport(configurer);
}
@RequestMapping(value = "/streamrecords/{elementname}", method = RequestMethod.GET,
produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<StreamingResponseBody> streamRecords(
HttpServletRequest request,
HttpServletResponse response,
@PathVariable(value = "elementname", required = true) String elementName,
@RequestParam(value = "customerid", required = true) long customerId,
@RequestParam(value = "userid", required = true) long userId) throws Exception {
StreamingResponseBody responseBody = outputStream -> {
/**
* 1. FETCH Data from MongoDB using dbcursor and convert to json using pagination.
* 2. Write json to outputStream.
*/
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=sample.json")
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody);
}
}
由于 WebMvcConfigurerAdapter
现已弃用,class 现在必须实施 WebMvcConfigurer
并且 Ankur 的答案已成功重写为:
@RestController
@RequestMapping("/api")
@Log4j2
public class StreamRecordsController implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(2_000_000);
}
...
使用 Spring Boot 2.4 进行测试。