Spring Boot @Async method in controller is executing synchronously

后端 未结 6 1706
天涯浪人
天涯浪人 2020-12-07 18:02

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 \"y

相关标签:
6条回答
  • I used spring-boot main class to define Asynchronous configuration. The @EnableAsync annotation enables Spring’s ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor(), since this is the specific method name searched by 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;
        }
    }
    

    In Implementation, use @Async at method level to make the method Asynchronous. Methods need to be public to use @Async. Also, @Async annotated method calling @Async method will not work.

    Sample implementation for reference below -

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

    The configuration properties are defined in application.properties file

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

    For Rules on how to define the pool, please refer to rules-of-a-threadpool-executor

    0 讨论(0)
  • 2020-12-07 18:12

    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 taskExecutor So...

    @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.html for the configuration available for the thread pool.

    0 讨论(0)
  • 2020-12-07 18:14

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

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

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

    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

    @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

    @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

    0 讨论(0)
  • 2020-12-07 18:24

    You are calling the @Async method 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 @Async method in another @Bean.

    0 讨论(0)
  • 2020-12-07 18:27

    As code sample for @dave-syer answer:

    This works asynchronously:

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

    But this doesn't:

    @Async
    private void longRunning() {
        try {
            log.info("wait 3 seconds");
            Thread.sleep(3000);
        } catch (InterruptedException e1) {
        }
        log.info("done");               
    }
    
    @Override
    public void doWork() {
        longRunning();
    }
    
    0 讨论(0)
  • 2020-12-07 18:33

    Follow the three steps :

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

    @EnableAsync public class Application {

    2 Step :

    /**
     * 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

    T

    0 讨论(0)
提交回复
热议问题