Java 控制器中的 Spring Boot @Async 方法正在同步执行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29284008/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:44:09  来源:igfitidea点击:

Spring Boot @Async method in controller is executing synchronously

javaajaxspring-mvcasynchronousspring-boot

提问by Web User

My [basic] Spring Boot application accepts a request from the browser, sent via jQuery.get()and is supposed to immediately receive a response - such as "your request has been queued". To accomplish this, I wrote a controller:

我的 [basic] Spring Boot 应用程序接受来自浏览器的请求,通过jQuery.get()它发送并应该立即收到响应 - 例如“您的请求已排队”。为此,我编写了一个控制器:

@Controller
public class DoSomeWorkController {

  @Autowired
  private final DoWorkService workService;

  @RequestMapping("/doSomeWork")
  @ResponseBody
  public String doSomeWork() {

    workService.doWork(); // time consuming operation
    return "Your request has been queued.";
  }
}

The DoWorkServiceImplclass implements a DoWorkServiceinterface and is really simple. It has a single method to perform a time consuming task. I don't need anything returned from this service call, as an email will be delivered at the end of the work, both for failure or success scenarios. So it would effectively look like:

DoWorkServiceImpl类实现一个DoWorkService接口,是很简单的。它有一个单一的方法来执行一项耗时的任务。我不需要此服务调用返回的任何内容,因为将在工作结束时发送一封电子邮件,无论是失败还是成功场景。所以它实际上看起来像:

@Service
public class DoWorkServiceImpl implements DoWorkService {

  @Async("workExecutor")
  @Override
  public void doWork() {

    try {
        Thread.sleep(10 * 1000);
        System.out.println("completed work, sent email");
    }
    catch (InterruptedException ie) {
        System.err.println(ie.getMessage());
    }
  }
}

I thought this would work, but the browser's Ajax request waited for 10 seconds before returning the response. So the controller mapped method is calling the internal method annotated with @Asyncsynchronously, it would seem. In a traditional Spring application, I typically add this to the XML configuration:

我认为这会起作用,但浏览器的 Ajax 请求在返回响应之前等待了 10 秒。因此,控制器映射方法正在@Async同步调用带有注释的内部方法,看起来。在传统的 Spring 应用程序中,我通常将其添加到 XML 配置中:

<task:annotation-driven />
<task:executor id="workExecutor" pool-size="1" queue-capacity="0" rejection-policy="DISCARD" />

So I thought writing the equivalent of this in the main application class would help:

所以我认为在主应用程序类中编写与此等效的内容会有所帮助:

@SpringBootApplication
@EnableAsync
public class Application {

  @Value("${pool.size:1}")
  private int poolSize;;

  @Value("${queue.capacity:0}")
  private int queueCapacity;

  @Bean(name="workExecutor")
  public TaskExecutor taskExecutor() {
      ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
      taskExecutor.setMaxPoolSize(poolSize);
      taskExecutor.setQueueCapacity(queueCapacity);
      taskExecutor.afterPropertiesSet();
      return taskExecutor;
  }

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

This did not change the behavior. The Ajax response still arrives after 10 seconds of sending the request. What am I missing?

这并没有改变行为。Ajax 响应在发送请求 10 秒后仍然到达。我错过了什么?

The Spring Boot application can be downloaded here. With Maven installed, the project can be run with the simple command:

Spring Boot 应用程序可以在这里下载。安装 Maven 后,可以使用简单的命令运行该项目:

mvn clean spring-boot:run

NoteThe issue was resolved thanks to the answer provided by @Dave Syer below, who pointed out that I was missing @EnableAsyncin my application, even though I had the line in the code snippet above.

注意由于下面@Dave Syer 提供的答案,该问题得到了解决,他指出@EnableAsync我的应用程序中丢失了,即使我在上面的代码片段中有一行。

采纳答案by Dave Syer

You are calling the @Asyncmethod from another method in the same class. Unless you enable AspectJ proxy mode for the @EnableAsync(and provide a weaver of course) that won't work (google "proxy self-invocation"). The easiest fix is to put the @Asyncmethod in another @Bean.

您正在@Async从同一类中的另一个方法调用该方法。除非您为@EnableAsync不起作用的(谷歌“代理自调用”)启用 AspectJ 代理模式(当然还提供编织器)。最简单的解决@Async方法是将该方法放在另一个@Bean.

回答by Carlos Andres

I had a similar issue and I had the annotations @Async and @EnableAsync in the correct beans and still the method was executing synchronously. After I checked the logs there was a warning saying that I had more than one bean of type ThreadPoolTaskExecutor and none of them called taskExecutorSo...

我有一个类似的问题,我在正确的 bean 中有 @Async 和 @EnableAsync 注释,但该方法仍在同步执行。在我检查日志后,有一条警告说我有多个 ThreadPoolTask​​Executor 类型的 bean,但没有一个 bean 称为taskExecutor所以......

@Bean(name="taskExecutor")
public ThreadPoolTaskExecutor defaultTaskExecutor() {
     ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
     //Thread pool configuration
     //...
     return pool;
}

See http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.htmlfor the configuration available for the thread pool.

有关线程池可用的配置,请参阅http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTask​​Executor.html

回答by Aj Tech Developer

For all those who are still looking for all the steps in @Asnyc explained in a simple way, here is the answer:

对于所有仍在寻找以简单方式解释的@Asnyc 中所有步骤的人,这里是答案:

Here is a simple example with @Async. Follow these steps to get @Async to work in your Spring Boot application:

这是@Async 的一个简单示例。按照以下步骤让 @Async 在您的 Spring Boot 应用程序中工作:

Step 1: Add @EnableAsync annotation and Add TaskExecutor Bean to Application Class.

步骤1:添加@EnableAsync注解并将TaskExecutor Bean添加到应用程序类。

Example:

例子:

@SpringBootApplication
@EnableAsync
public class AsynchronousSpringBootApplication {

    private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);

    @Bean(name="processExecutor")
    public TaskExecutor workExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("Async-");
        threadPoolTaskExecutor.setCorePoolSize(3);
        threadPoolTaskExecutor.setMaxPoolSize(3);
        threadPoolTaskExecutor.setQueueCapacity(600);
        threadPoolTaskExecutor.afterPropertiesSet();
        logger.info("ThreadPoolTaskExecutor set");
        return threadPoolTaskExecutor;
    }

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

Step 2: Add Method which executes an Asynchronous Process

第 2 步:添加执行异步进程的方法

@Service
public class ProcessServiceImpl implements ProcessService {

    private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Async("processExecutor")
    @Override
    public void process() {
        logger.info("Received request to process in ProcessServiceImpl.process()");
        try {
            Thread.sleep(15 * 1000);
            logger.info("Processing complete");
        }
        catch (InterruptedException ie) {
            logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
        }
    }
}

Step 3: Add an API in the Controller to execute the asynchronous processing

第三步:在Controller中添加API来执行异步处理

@Autowired
private ProcessService processService;

@RequestMapping(value = "ping/async", method = RequestMethod.GET)
    public ResponseEntity<Map<String, String>> async() {
        processService.process();
        Map<String, String> response = new HashMap<>();
        response.put("message", "Request is under process");
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

I have also written a blog and a working application on GitHub with these steps. Please check: http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html

我还在 GitHub 上用这些步骤写了一个博客和一个工作应用程序。请查看:http: //softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html

回答by Sanjeet Shrivastava

Follow the three steps :

遵循三个步骤:

1 Step : Use @EnableAsync with @configuration or @SpringBootApplication

1 步:将@EnableAsync 与@configuration 或@SpringBootApplication 一起使用

@EnableAsync public class Application {

@EnableAsync 公共类应用程序 {

2 Step :

2 步:

/**
 * THIS FOR ASYNCRONOUS PROCESS/METHOD
 * @return
 */
@Bean
public Executor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(500);
    executor.setThreadNamePrefix("Asynchronous Process-");
    executor.initialize();
    return executor;
}

3 Step : Put @Async over the intended method

第 3 步:将 @Async 放在预期的方法上

T