简体   繁体   English

如何使用Springboot向控制器实现异步REST请求?

[英]How to implement an asynchronous REST request to a controller using Springboot?

I'm trying to implement an asynchronous controller using SprintBoot. 我正在尝试使用SprintBoot实现异步控制器。 I want to make REST request to a controller so that the controller returns immediately, while the work continues on the server. 我想向控制器发出REST请求,以便控制器立即返回,同时在服务器上继续工作。

I'm following this Spring example: http://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support 我正在关注这个Spring示例: http//spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support

I suspect this is a configuration problem. 我怀疑这是一个配置问题。 Can someone please tell me what I'm missing? 有人可以告诉我我错过了什么吗? I'm new to Spring so if you could please provide as much details as possible it would be appreciated. 我是Spring的新手,所以如果你能提供尽可能多的详细信息,我们将不胜感激。

Using a working controller I made the following changes: 使用工作控制器我做了以下更改:

// 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";
    }
  };
}

I am able to call the new controller but I have two issues below: 我可以调用新的控制器,但我有以下两个问题:

  1. The new controller is not called asynchronously. 不会异步调用新控制器。 The browser hangs during the call. 浏览器中的浏览器挂起。 The call does execute the code though. 该调用确实执行代码。
  2. The request times out with this error: 请求超时时出现此错误:

    2015-03-06 16:36:10.592 ERROR 13012 --- [ MvcAsync1] oswcrequest.async.WebAsyncManager : Could not complete async processing due to timeout or network error 2015-03-06 16:36:10.592 ERROR 13012 --- [MvcAsync1] oswcrequest.async.WebAsyncManager:由于超时或网络错误导致无法完成异步处理

Update: I was able to resolve the timeout by creating the following bean in my application file: 更新:我能够通过在我的应用程序文件中创建以下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;
    }

But the call the to the controller is still not asynchronous. 但是对控制器的调用仍然不是异步的。 The browser still hangs for the duration of the call. 浏览器在呼叫期间仍会挂起。

I'm still looking for help on how to make a the REST call to the controller return immediately while doing the work in the background. 我仍然在寻找有关如何在后台进行工作时立即对控制器进行REST调用的帮助。

Update II 更新II

Thank you Dave. 谢谢戴夫。 I have attempted to implement an async method in a bean. 我试图在bean中实现异步方法。

Here is my application 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;
    }
 }

Here is my bean class: 这是我的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;
        }
    }

}

My configuration 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();
    }

}

My controller method: 我的控制器方法:

@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}";

    }

This unfortunately still does not return immediately. 遗憾的是,这仍然不会立即返回。 Note that I don't care about the result from LongProcess. 请注意,我不关心LongProcess的结果。 The method is called successfully, but not in the background. 该方法被成功调用,但不在后台调用。 Any idea what I might be missing? 知道我可能会缺少什么吗?

As a test, if I change the controller method to wait for the result, the wait block is never entered: 作为测试,如果我更改控制器方法以等待结果,则永远不会输入等待块:

@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}";

    }

Update III 更新III

I replaced 我换了

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

with

@Autowired
private LongProcess process;

and this solved the issue. 这解决了这个问题。

I think you misunderstand the MVC async (and Servlet 3) features. 我认为你误解了MVC异步(和Servlet 3)的功能。 If your controller method takes a long time to complete it will be called in a different thread than the one used to handle the incoming request, but it still has to return data to the client on the same HTTP connection, so it can time out from that point of view. 如果您的控制器方法需要很长时间才能完成,它将在与用于处理传入请求的线程不同的线程中调用,但它仍然必须在同一HTTP连接上将数据返回给客户端,因此它可能会超时那个观点。 To return immediately but do the processing in the background you don't need async MVC, you just need to do the expensive processing in a background thread (eg by calling an @Async method in another @Bean ). 要立即返回但在后台进行处理,您不需要异步MVC,只需在后台线程中进行昂贵的处理(例如,通过在另一个@Bean调用@Async方法)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM