Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.6k views
in Technique[技术] by (71.8m points)

asynchronous - Async timeout downloading a large file using StreamingResponseBody on Spring Boot

I'm trying to expose a REST Service which makes available to download a large file streaming, without keeping in memory first. Also I need this to support async calls, if (at least) two users on the same time call this URL should be able both of them to download it. Application is set up with Spring Boot.
This is what I have on Controller:

@RestController
public class MyController {

   private MyService service;

   @Autowired
   public MyController(MyService service) {
       this.service = service;
   }

   @RequestMapping(
        value = "download",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
   public ResponseEntity<StreamingResponseBody> downloadAsync() throws IOException {

       StreamingResponseBody responseBody = outputStream -> {
           service.download(outputStream);
           outputStream.close();
       };

       return ResponseEntity.ok(responseBody);
    }
}

This is what I have on Service (download URL is just a sample to test this behavior):

@Service
public class MyService {

    private RestTemplate restTemplate;

    @Autowired
    public MyService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public void download(OutputStream outputStream) {

        ResponseExtractor<Void> responseExtractor = clientHttpResponse -> {
            InputStream inputStream = clientHttpResponse.getBody();
            StreamUtils.copy(inputStream, outputStream);
            return null;
        };

        restTemplate.execute("http://download.thinkbroadband.com/1GB.zip",
            HttpMethod.GET,
            clientHttpRequest -> {},
            responseExtractor);
    }
}

In application.yml among others, I have these properties, nothing fancy at all:

server:
  port: 9999
  context-path: /rest

And this is the JavaConfig file:

@Configuration
public class ApplicationConfig {
    @Bean
    public RestTemplate restTemplate() {
        ClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());

        RestTemplate restTemplate = new RestTemplate(requestFactory);
        restTemplate.setErrorHandler(new ClientErrorHandler());
        return restTemplate;
    }
}

When I call this endpoint localhost:9999/rest/download download starts and downloads some MBs but after some time, it stops and this is what gets shown on my console:

2017-03-18 17:11:54.808 INFO  --- [nio-9999-exec-1] o.a.c.c.C.[.[.[/rest]                    : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-03-18 17:11:54.811 INFO  --- [nio-9999-exec-1] o.s.w.s.DispatcherServlet                : FrameworkServlet 'dispatcherServlet': initialization started
2017-03-18 17:11:54.895 INFO  --- [nio-9999-exec-1] o.s.w.s.DispatcherServlet                : FrameworkServlet 'dispatcherServlet': initialization completed in 84 ms
2017-03-18 17:12:25.334 ERROR --- [nio-9999-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Async timeout for GET [/rest/download]
2017-03-18 17:12:25.335 WARN  --- [nio-9999-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.context.request.async.AsyncRequestTimeoutException
2017-03-18 17:12:25.366 INFO  --- [nio-9999-exec-2] o.a.c.c.CoyoteAdapter                    : Encountered a non-recycled response and recycled it forcedly.
org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException: null
    at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:494) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.http11.Http11Processor.recycle(Http11Processor.java:1627) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.release(AbstractProtocol.java:977) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:869) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_60]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_60]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.11.jar:8.5.11]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_60]

Can anyone help, please ? Thanks in advance

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you are encountering this issue using Spring-Boot, it is enough to set the following property to a higher value - for example:

spring:
  mvc:
    async:
      request-timeout: 3600000

or

spring.mvc.async.request-timeout = 3600000

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...