Как реализовать асинхронный запрос REST контроллеру с помощью Springboot?

Я пытаюсь реализовать асинхронный контроллер с помощью SprintBoot. Я хочу сделать запрос REST контроллеру, чтобы контроллер немедленно вернулся, а работа продолжалась на сервере.

Я следую этому примеру Spring: http://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support

Я подозреваю, что это проблема конфигурации. Может кто-нибудь, пожалуйста, скажите мне, что мне не хватает? Я новичок в Spring, поэтому, если бы вы могли представить как можно больше деталей, это было бы оценено.

Используя рабочий контроллер, я сделал следующие изменения:

// Before
@RequestMapping(method=RequestMethod.POST)
public String processUpload(final MultipartFile file) {
    // ...
    return "someView";
}

// After
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {

  return new Callable<String>() {
    public Object call() throws Exception {
      // ...
      return "someView";
    }
  };
}

Я могу назвать новый контроллер, но у меня есть два вопроса ниже:

  1. Новый контроллер не вызывается асинхронно. Во время разговора браузер зависает. Однако вызов выполняет код.
  2. Запрос с этой ошибкой:

    2015-03-06 16: 36: 10.592 ОШИБКА 13012 --- [MvcAsync1] oswcrequest.async.WebAsyncManager: не удалось завершить обработку асинхронного сообщения из-за таймаута или сетевой ошибки

Обновление: мне удалось решить тайм-аут, создав следующий компонент в моем файле приложения:

@Bean
public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

            @Override
            public void customize(Connector connector) {

                connector.setPort(9000);
                connector.setAsyncTimeout(60000);
            }
        });
        return factory;
    }

Но вызов к контроллеру по-прежнему не является асинхронным. Браузер все еще висит на время вызова.

Я по-прежнему ищу помощь в том, как немедленно вызвать вызов REST контроллеру, выполняя работу в фоновом режиме.

Обновление II

Спасибо, Дейв. Я попытался реализовать метод async в bean-компоненте.

Вот мой класс приложения:

@SpringBootApplication
@EnableAsync
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

            @Override
            public void customize(Connector connector) {

                connector.setPort(9000);
                connector.setAsyncTimeout(60000);
            }
        });
        return factory;
    }
 }

Вот мой класс bean:

public class LongProcess {

    @Async
    public Future<String> call() {
        try {
            System.out.println("Sleeping now...");
            Thread.sleep(10000);
            return new AsyncResult<String>("Hey");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
    }

}

Мой класс конфигурации:

@Configuration
@EnableAsync
public class LongProcessConfiguration implements AsyncConfigurer {

    @Bean
    public LongProcess longProcessBean() {
        return new LongProcess();
    }

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setThreadNamePrefix("LULExecutor-");
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }

}

Мой метод управления:

@RequestMapping("/utilities/longProcess")
    public String longProcess() {

        System.out.println("Starting long process...");
        CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
        LongProcess process = context.longProcessBean();
        Future<String> result = process.call();
        System.out.println("Done!");
        return "{success: 1}";

    }

К сожалению, это, к сожалению, не сразу возвращается. Обратите внимание, что меня не волнует результат LongProcess. Метод называется успешно, но не в фоновом режиме. Любая идея, что я могу пропустить?

В качестве теста, если я изменю метод контроллера для ожидания результата, блок ожидания никогда не вводится:

@RequestMapping("/utilities/longProcess")
    public String longProcess() throws InterruptedException {

        System.out.println("Starting long process...");
        CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
        LongProcess process = context.longProcessBean();
        Future<String> result = process.call();
        while (!(result.isDone())) {
            Thread.sleep(1); //10-millisecond pause between each check
            System.out.println("Waiting for Long Process...");
        }
        System.out.println("Done!");
        return "{success: 1}";

    }

Обновление III

Я заменил

CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
            LongProcess process = context.longProcessBean();

с

@Autowired
private LongProcess process;

и это решило проблему.

Ответы

Ответ 1

Я думаю, вы неправильно понимаете функции MVK async (и Servlet 3). Если ваш метод контроллера занимает много времени, он будет вызываться в другом потоке, чем тот, который используется для обработки входящего запроса, но он все равно должен возвращать данные клиенту по одному и тому же HTTP-соединению, поэтому он может время от времени эта точка зрения. Для немедленного возврата, но для обработки в фоновом режиме вам не нужен async MVC, вам просто нужно выполнить дорогостоящую обработку в фоновом потоке (например, вызывая метод @Async в другом @Bean).