Asynchronous framework
Why use asynchronous framework? Solve what problem?
In the development of spring boot, it is generally called synchronously. However, there are often special businesses that need to be processed asynchronously.
For example: register a new user, send 100 points, or place a successful order, send a push message, etc.
Why do you register new users asynchronously?
- The first reason: fault tolerance and robustness. If there is an exception in sending points, the user registration cannot fail because of sending points;
Because user registration is the main function and sending points is the secondary function, even if the sending of points is abnormal, the user should be prompted to register successfully, and then compensation will be made for the abnormal points. - The second reason: improve performance. For example, it takes 20 milliseconds to register users and 50 milliseconds to send points. If synchronous, it takes 70 milliseconds. If asynchronous, it doesn't need to wait for points, so it takes 20 milliseconds.
Therefore, asynchronous can solve two problems, performance and fault tolerance
Application examples
You only need to use the @ Async annotation to implement the asynchronous call of the method.
@Example of Async asynchronous call:
Step 1: start asynchronous task
Use @ EnableAshnc to start asynchronous tasks;
It is easy to inject this class into ioc using @ configuration;
@Configuration @EnableAsync public class AsyncConfiguration{ }
Step 2: mark the asynchronous call controller layer on the method
@RestController @Slf4j public class TestController{ @Autowired private ScoreService scoreService; @RequestMapping("/sync") public String createUser() { log.info("--------------Registered user--------------------"); this.scoreService.addScore(); return "OK"; } @RequestMapping("/sync2") public String createUser2() { log.info("--------------Registered user 2--------------------"); this.scoreService.addScore2(); return "OK"; } }
service layer: add @ Async annotation on the method to indicate that the method is called asynchronously
@Service @Slf4j public class ScoreService{ @Async public void addScore(){ try{ Thread.sleep(10000); log.info("Add points"); } catch(Exception e){} } @Async public void addScore2(){ try{ Thread.sleep(10000) }catch(Exception e){} } }
@The underlying principle of Async thread pool configuration
@The Async annotation uses the SimpleAsyncTaskExecutor thread pool by default, which is not a true thread pool; Because threads are not reused and destroyed after use, a thread will be created for each call.
And distributed, resulting in a waste of resources and reducing the system, because there are two more steps of creation and destruction, which is time-consuming.
@Async annotates a variety of thread pools provided by the asynchronous framework
SimpleAsyncTaskExecutor: it is not a real thread pool. This class does not reuse threads. Each call will create a new thread.
@Async annotates a variety of thread pools provided by the asynchronous framework:
- SimpleAsyncTaskExecutor: it is not a real thread pool. This class does not reuse threads. Each call will create a new thread.
- SyncTaskExecutor: this class does not implement asynchronous call, but a synchronous operation.
- ConcurrentTaskExecutor: the adapter class of Executor, which is not recommended. Consider using this class only if ThreadPoolTaskExecutor does not meet the requirements
- ThreadPoolTaskScheduler: you can use cron expressions
- ThreadPoolTaskExecutor: most commonly used, recommended. Its essence is to Java util. concurrent. Wrappers for ThreadPoolExecutor (jdk's own thread pool)
Implement a custom thread pool for @ Async
Step 1
@Configuraion @EnableAsync public class AsyncConfiguration { @Bean(name="scorePoolTaskExecutor") public ThreadPoolTaskExecutor getScorePoolTaskExecutor(){ //Create thread pool ThreadPoolTaskExecutor taskExecutor=new ThreadPoolTaskExecutor(); //Number of core threads taskExecutor.setCorePoolSize(10); //The thread pool maintains the maximum number of threads. Threads exceeding the number of core threads will be applied only after the buffer queue is full taskExecutor.setMaxPoolSize(100); //Cache queue taskExecutor.setQueueCapacity(50); //The allowed idle time. When the idle time exceeds that of the core thread, the thread will be destroyed after the idle time arrives taskExecutor.setKeepAliveSeconds(200); //Asynchronous method internal thread name taskExecutor.setThreadNamePrefix("score-"); /** * When the task cache queue of the thread pool is full and the number of threads in the thread pool reaches maximumPoolSize, the task rejection policy will be adopted if there are still tasks coming * There are usually four strategies: * ThreadPoolExecutor.AbortPolicy:Discard the task and throw a RejectedExecutionException exception. * ThreadPoolExecutor.DiscardPolicy: It also discards the task without throwing an exception. * ThreadPoolExecutor.DiscardOldestPolicy: Discard the task at the top of the queue and try to execute the task again (repeat this process) * ThreadPoolExecutor.CallerRunsPolicy: Retry adding the current task, and automatically call the execute() method repeatedly until it succeeds */ taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); taskExecutor.initialize(); return taskExecutor; } }
Step 2: specify the thread pool name for @ Async
@Async("scorePoolTaskExecutor") public void addScore2(){ try { Thread.sleep(1000*5); log.info("--------------Processing integral--------------------"); } catch (InterruptedException e) { e.printStackTrace(); } }
In real Internet project development, for high concurrency requests, the general practice is to isolate the high concurrency interface from a separate thread pool.