如何修复 - 需要 Executor 来处理 java.util.concurrent.Callable 返回值

ero*_*ppa 9 spring spring-mvc spring-boot

我在 Spring Boot/Spring Data Rest 中有一个控制器,我的处理程序在其中下载这样的文件

@RequestMapping(method = GET, value = "/orderAttachments/{id}/download")
    @ResponseStatus(HttpStatus.OK)
    public ResponseEntity<StreamingResponseBody> downloadAttachment(@PathVariable("id") Long attachmentID, HttpServletRequest request)
            throws IOException {

        InputStream inputStream = fileManager.getInputStream(orderAttachment);

        StreamingResponseBody responseBody = outputStream -> {

            int numberOfBytesToWrite;
            byte[] data = new byte[1024];
            while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
                outputStream.write(data, 0, numberOfBytesToWrite);
            }

            inputStream.close();
        };

        return ResponseEntity
                .ok()
                .contentLength(orderAttachment.getFileSize())
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""  + orderAttachment.getFileName()+ "\"")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(responseBody);
    }
Run Code Online (Sandbox Code Playgroud)

我在控制台中收到此错误

!!!
An Executor is required to handle java.util.concurrent.Callable return values.
Please, configure a TaskExecutor in the MVC config under "async support".
The SimpleAsyncTaskExecutor currently in use is not suitable under load.
-------------------------------
Request URI: '/api/v1/orderAttachments/163/download'
!!!
Run Code Online (Sandbox Code Playgroud)

但一切正常,我可以通过调用 API 下载文件

M. *_*num 7

对于初学者来说,这是一个警告而不是错误。如果这是一个错误,您的应用程序将无法启动或工作。这是一个警告,告诉您SimpleAsyncTaskExecutor不应在生产中使用所使用的默认值。

SimpleAsyncTaskExecutor创建一个新的Thread时候有什么需要被异步处理。每个线程默认占用 1MB 内存(和一些处理能力)。现在想象有人向这个 API 发出 100000 次调用。这也意味着 100000 个线程(乘以内存和每个 CPU 功率)。它会削弱您的应用程序,甚至可能会杀死它。

注意:如果您使用的是 Spring Boot 2.1.x 或更高版本,TaskExecutor则会为您配置和使用默认值。但是,如果您的配置中有禁用自动配置的内容(例如@EnableWebMvc@Configuration类上)。要配置该执行程序(使用线程等),请查看Spring Boot 参考指南

您还可以手动创建一个TaskExecutor并将其与 Spring MVC 相关联。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  @Bean
  public ThreadPoolTaskExecutor mvcTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(10);
    taskExecutor.setMaxPoolSize(10); 
    return taskExecutor;
  }

  public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    configurer.setTaskExecutor(mvcTaskExecutor());
  }

}
Run Code Online (Sandbox Code Playgroud)

现在这个专用的TaskExecutor将用于 Spring MVC 中的异步处理。但是,如前所述,如果您没有禁用自动配置(甚至可能是偶然的!),Spring Boot 应该已经预先配置了它。


Pio*_*aza 3

这只是一个警告(这就是为什么您仍然能够下载请求的文件),Spring 建议定义您自己的文件,TaskExecutor而不是使用该SimpleAsyncTaskExecutor文件,正如消息所述,在负载下不适合。

SimpleAsyncTaskExecutor为每个任务启动一个新线程并且不重用它们。如果您没有配置并发线程限制,则默认为无限制。

@Configuration
class AsyncConfiguration implements AsyncConfigurer {

    @Bean
    protected WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurerAdapter() {

            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setTaskExecutor([your task executor]);
            }

        };
    }

}
Run Code Online (Sandbox Code Playgroud)

使用上面的配置来定义TaskExecutor适合您需要的配置并消除警告。