如何使用 Spring Boot 实现对控制器的异步 REST 请求?
How to implement an asynchronous REST request to a controller using Springboot?
我正在尝试使用 SprintBoot 实现异步控制器。我想向控制器发出 REST 请求,以便控制器立即 returns,而服务器上的工作继续进行。
我正在关注这个 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] o.s.w.c.request.async.WebAsyncManager:由于超时或网络错误无法完成异步处理
更新:
我能够通过在我的应用程序文件中创建以下 bean 来解决超时问题:
@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 调用的帮助return。
更新二
谢谢戴夫。我试图在 bean 中实现异步方法。
这是我的申请 class:
@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;
}
}
这是我的豆子class:
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;
}
}
}
我的配置class:
@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}";
}
不幸的是,这仍然不会立即 return。请注意,我不关心 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}";
}
更新三
我换了
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
和
@Autowired
private LongProcess process;
这解决了问题。
我认为您误解了 MVC 异步(和 Servlet 3)功能。如果您的控制器方法需要很长时间才能完成,它将在与用于处理传入请求的线程不同的线程中调用,但它仍然必须 return 在同一 HTTP 连接上向客户端发送数据,因此它可以从那个角度超时。要立即 return 但在后台进行处理,您不需要异步 MVC,您只需要在后台线程中进行昂贵的处理(例如,通过在另一个 [=11] 中调用 @Async
方法=]).
我正在尝试使用 SprintBoot 实现异步控制器。我想向控制器发出 REST 请求,以便控制器立即 returns,而服务器上的工作继续进行。
我正在关注这个 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] o.s.w.c.request.async.WebAsyncManager:由于超时或网络错误无法完成异步处理
更新: 我能够通过在我的应用程序文件中创建以下 bean 来解决超时问题:
@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 调用的帮助return。
更新二
谢谢戴夫。我试图在 bean 中实现异步方法。
这是我的申请 class:
@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;
}
}
这是我的豆子class:
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;
}
}
}
我的配置class:
@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}";
}
不幸的是,这仍然不会立即 return。请注意,我不关心 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}";
}
更新三
我换了
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
和
@Autowired
private LongProcess process;
这解决了问题。
我认为您误解了 MVC 异步(和 Servlet 3)功能。如果您的控制器方法需要很长时间才能完成,它将在与用于处理传入请求的线程不同的线程中调用,但它仍然必须 return 在同一 HTTP 连接上向客户端发送数据,因此它可以从那个角度超时。要立即 return 但在后台进行处理,您不需要异步 MVC,您只需要在后台线程中进行昂贵的处理(例如,通过在另一个 [=11] 中调用 @Async
方法=]).