簡體   English   中英

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

[英]Spring Boot @Async method in controller is executing synchronously

我的 [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.";
  }
}

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

我認為這會起作用,但瀏覽器的 Ajax 請求在返回響應之前等待了 10 秒。 因此,控制器映射方法正在同步調用用@Async注釋的內部方法,看起來。 在傳統的 Spring 應用程序中,我通常將其添加到 XML 配置中:

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

所以我認為在主應用程序類中編寫與此等效的內容會有所幫助:

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

這並沒有改變行為。 Ajax 響應在發送請求 10 秒后仍然到達。 我錯過了什么?

Spring Boot 應用程序可以在這里下載 安裝 Maven 后,可以使用簡單的命令運行該項目:

mvn clean spring-boot:run

注意由於下面@Dave Syer 提供的答案,該問題得到了解決,他指出我的應用程序中缺少@EnableAsync ,即使我在上面的代碼片段中有一行。

您正在從同一類中的另一個方法調用@Async方法。 除非您為@EnableAsync啟用 AspectJ 代理模式(當然還提供編織器),否則將無法工作(谷歌“代理自調用”)。 最簡單的解決方法是將@Async方法放在另一個@Bean

對於所有仍在尋找以簡單方式解釋的@Asnyc 中所有步驟的人,這里是答案:

這是@Async 的一個簡單示例。 按照以下步驟讓 @Async 在您的 Spring Boot 應用程序中工作:

步驟1:添加@EnableAsync注解並將TaskExecutor Bean添加到應用程序類。

例子:

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

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

第三步:在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);
    }

我還在 GitHub 上用這些步驟寫了一個博客和一個工作應用程序。 請查看: http : //softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html

我有一個類似的問題,我在正確的 bean 中有 @Async 和 @EnableAsync 注釋,但該方法仍在同步執行。 在我檢查日志后,有一條警告說我有多個 ThreadPoolTask​​Executor 類型的 bean,但沒有一個 bean 稱為taskExecutor所以......

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

有關線程池可用的配置,請參閱http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTask​​Executor.html

遵循三個步驟:

1 步:將@EnableAsync 與@configuration 或@SpringBootApplication 一起使用

@EnableAsync 公共類應用程序 {

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 步:將 @Async 放在預期的方法上

作為@dave-syer 答案的代碼示例:

這是異步工作的:

private void longRunning() {
    try {
        log.info("wait 3 seconds");
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
    }
    log.info("done");               
}

@Async  
@Override
public void doWork() {
    longRunning();
}

但這不會:

@Async
private void longRunning() {
    try {
        log.info("wait 3 seconds");
        Thread.sleep(3000);
    } catch (InterruptedException e1) {
    }
    log.info("done");               
}

@Override
public void doWork() {
    longRunning();
}

我使用 spring-boot 主類來定義異步配置 @EnableAsync注釋使 Spring 能夠在后台線程池中運行@Async方法。 這個類還通過定義一個新的 bean 來自定義 Executor。 此處,該方法名為taskExecutor() ,因為這是 Spring 搜索的特定方法名稱。

Spring-Boot-Application.class

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

    @Value("${config.threadpool.corepool.size}")
    private Integer corePoolSize;

    @Value("${config.threadpool.maxpool.size}")
    private Integer maxPoolSize;

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

    //name of below method should not be changed.
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        //other proeprties to be set here
        executor.setThreadNamePrefix("ASYNC-");
        executor.initialize();
        return executor;
    }
}

在實現中,在方法級別使用@Async使方法異步。 方法需要公開才能使用@Async 此外,調用@Async方法的@Async注釋方法將不起作用。

以下示例實現供參考 -

@Async
  public void updateData(String userId) throws ApplicationException {
    logger.info("Updating details for User with {}", userId);
    //your code goes here...
  }

配置屬性在application.properties文件中定義

#Core Pool Size for Async
config.threadpool.corepool.size=100
#Max Pool Size for Async
config.threadpool.maxpool.size=400   

關於如何定義池的規則,請參考rules-of-a-threadpool-executor

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM