Как реализовать асинхронный запрос 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";
}
};
}
Я могу назвать новый контроллер, но у меня есть два вопроса ниже:
- Новый контроллер не вызывается асинхронно. Во время разговора браузер зависает. Однако вызов выполняет код.
-
Запрос с этой ошибкой:
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
).