异步超时在 Spring Boot 上使用 StreamingResponseBody 下载大文件



我正在尝试公开一个 REST 服务,该服务可以下载大型文件流,而无需先保留在内存中。我也需要它来支持异步调用,如果(至少(两个用户同时调用此 URL 应该能够同时下载它。应用程序使用 Spring Boot 进行设置。
这是我在控制器上拥有的:

@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);
    }
}

这是我在服务上的内容(下载 URL 只是测试此行为的示例(:

@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);
    }
}

application.yml中,我有这些属性,一点也不花哨:

server:
  port: 9999
  context-path: /rest

这是Java配置文件:

@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;
    }
}

当我调用此端点时localhost:9999/rest/download下载开始并下载一些 MB,但一段时间后,它会停止,这就是我的控制台上显示的内容:

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]

请问谁能帮忙?提前致谢

如果您在使用 Spring-Boot 时遇到此问题,将以下属性设置为更高的值就足够了 - 例如:

spring:
  mvc:
    async:
      request-timeout: 3600000

spring.mvc.async.request-timeout = 3600000
<</div> div class="one_answers">

您的异步任务执行器似乎遇到了超时问题。您可以使用 WebMvcConfigurerAdapter 配置所需的超时(和其他设置(。此代码应有助于解决此问题。请务必将省略号 (...( 替换为所需的值。

此示例还注册了一个拦截器,该侦听器在超时时被调用,以防您需要一些特殊处理。

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
    @Override
    @Bean(name = "taskExecutor")
    public AsyncTaskExecutor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(...);
        executor.setMaxPoolSize(...);
        executor.setQueueCapacity(...);
        executor.setThreadNamePrefix(...);
        return executor;
    }
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
    /** Configure async support for Spring MVC. */
    @Bean
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(AsyncTaskExecutor taskExecutor, CallableProcessingInterceptor callableProcessingInterceptor) {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setDefaultTimeout(...)
                    .setTaskExecutor(taskExecutor);
                configurer.registerCallableInterceptors(callableProcessingInterceptor);
                super.configureAsyncSupport(configurer);
            }
        };
    }
    @Bean
    public CallableProcessingInterceptor callableProcessingInterceptor() {
        return new TimeoutCallableProcessingInterceptor() {
            @Override
            public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
                log.error("timeout!");
                return super.handleTimeout(request, task);
            }
        };
    }
}

相关内容

  • 没有找到相关文章

最新更新