programing

Spring Boot에서 StreamingResponseBody를 사용하여 대용량 파일을 다운로드하는 비동기 시간 초과

closeapi 2023. 6. 20. 21:38
반응형

Spring Boot에서 StreamingResponseBody를 사용하여 대용량 파일을 다운로드하는 비동기 시간 초과

먼저 메모리에 저장하지 않고 대용량 파일 스트리밍을 다운로드할 수 있는 REST 서비스를 노출하려고 합니다.또한 두 명의 사용자가 동시에 이 URL을 호출할 경우 비동기 호출을 지원하기 위해 이 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

다음은 JavaConfig 파일입니다.

@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

비동기 작업 실행기에서 시간 초과 문제가 발생한 것 같습니다.원하는 시간 초과(및 기타 설정)를 구성할 수 있습니다.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);
            }
        };
    }
}

언급URL : https://stackoverflow.com/questions/42877498/async-timeout-downloading-a-large-file-using-streamingresponsebody-on-spring-boo

반응형