The original link: www.cnblogs.com/baixianlong…
Use of asynchronous requests in SpringBoot
1. Asynchronous versus synchronous requests
Features:
-
You can release the threads and related resources allocated by the container to the request first, reducing the burden on the system. If you release the request from the container, its response will be delayed and can respond to the client after the time-consuming processing (such as a long operation) is complete.
In a word: increased server throughput for client requests(In practice, we use very few requests. If there is a large number of concurrent requests, we will use Nginx to load the requests to the various nodes of the cluster service, and of course, we can also use message queues to buffer the requests.)
2. Implementation of asynchronous request
Method 1: Asynchronous requests are implemented using servlets
@RequestMapping(value = "/email/servletReq", method = GET) public void servletReq (HttpServletRequest request, HttpServletResponse response) { AsyncContext asyncContext = request.startAsync(); // Set listener: can set its start, finish, exception, timeout events callback processing asyncContext.addListener(new)AsyncListener() { @Override public void onTimeout(AsyncEvent event) throws IOException { System.out.println("I'm out of time..."); // Do some timeout related operations... } @Override public void onStartAsync(AsyncEvent event) throws IOException { System.out.println("Thread started"); } @Override public void onError(AsyncEvent event) throws IOException { System.out.println("Error:"+event.getThrowable()); } @Override public void onComplete(AsyncEvent event) throws IOException { System.out.println("Execution completed"); // You can do some cleanup here... }}); // setTimeout asynccontext. setTimeout(20000); asyncContext.start(newRunnable() { @Override public void run() { try { Thread.sleep(10000); System.out.println("Internal thread:" + Thread.currentThread().getName()); asyncContext.getResponse().setCharacterEncoding("utf-8"); asyncContext.getResponse().setContentType("text/html; charset=UTF-8"); asyncContext.getResponse().getWriter().println("This is an asynchronous request return."); } catch (Exception e) { System.out.println("Exception:"+e); } // asyncContext.complete(); }}); // The thread connection of such request has been released system.out.println ("Main thread:" + Thread.currentThread().getName()); }Copy the code
The default thread pool and timeout handling can be set by inheriting the WebMvcConfigurerAdapter class
@RequestMapping(value = "/email/callableReq", method = GET) @ResponseBody public Callable<String> callableReq () { System.out.println("External thread:" + Thread.currentThread().getName()); return new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(10000); System.out.println("Internal thread:" + Thread.currentThread().getName()); return "callable!"; }}; } @Configuration public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter { @Resource private ThreadPoolTaskExecutor myThreadPoolTaskExecutor; @Override public void configureAsyncSupport(Final AsyncSupportConfigurer configurer) {// Processing a callable timeout configurer.setDefaultTimeout(60*1000); configurer.setTaskExecutor(myThreadPoolTaskExecutor); configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor()); } @Bean public TimeoutCallableProcessingInterceptortimeoutCallableProcessingInterceptor() { return new TimeoutCallableProcessingInterceptor(); }Copy the code
}
Method 3: Similar to method 2, set a timeout callback to WebAsyncTask on the first layer of Callable, to achieve timeout processing
@RequestMapping(value = "/email/webAsyncReq", method = GET)
@ResponseBody
public WebAsyncTask<String> webAsyncReq () {
System.out.println("External thread:" + Thread.currentThread().getName());
Callable<String> result = () -> {
System.out.println("Internal thread started:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
// TODO: handle exception
}
logger.info("Secondary thread return");
System.out.println("Internal thread returns:" + Thread.currentThread().getName());
return "success";
};
WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
wat.onTimeout(new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Timeout"; }});return wat;
}Copy the code
DeferredResult can handle some complicated business logic, but most of all, it can be processed and returned in another thread, which can communicate between two completely unrelated threads.
@RequestMapping(value = "/email/deferredResultReq", method = GET)
@ResponseBody
public DeferredResult<String> deferredResultReq () {
System.out.println("External thread:"+ Thread.currentThread().getName()); New DeferredResult<String> result = new DeferredResult<String>(60*1000L); Result. onTimeout(new) {result.onTimeout(new)Runnable() {
@Override
public void run() {
System.out.println("DeferredResult timeout");
result.setResult("Out of time!"); }}); result.onCompletion(newRunnable() {
@Override
public void run() {// finish system.out.println ()"Call completed"); }}); myThreadPoolTaskExecutor.execute(newRunnable() {
@Override
public void run() {// process the business logic system.out.println ()"Internal thread:"+ Thread.currentThread().getName()); Result.setresult ("DeferredResult!!"); }});return result;
}Copy the code
The use of asynchronous calls in SpringBoot
1, the introduction
Processing of asynchronous requests. In addition to asynchronous requests, we generally use asynchronous calls more often. Often during development, you will encounter a method that is irrelevant to the real business and has no tightness. For example, recording log information. In this case, it is normal to start a new thread to do some business, and let the main thread asynchronously perform other business.
2. Usage (based on Spring)
- You need to add @enableAsync to the launch class to make the asynchronous call @async annotation work
- Add this annotation to methods that need to be executed asynchronously @async (“threadPool”). Threadpools are custom thread pools
-
Code omitted… Just two tags. Just try one on yourself
3. Precautions
- By default, when TaskExecutor is not set, the SimpleAsyncTaskExecutor thread pool is used by default, but this thread is not a true thread pool because threads are not reused and a new thread is created with each call. As you can see from the console log output, the thread name is incremented each time. So it’s best to customize a thread pool.
-
The asynchronous method called cannot be a method of the same class (including the inner class of the same class), simply because Spring creates a proxy class for it when it starts the scan, and the same invocation is made to its own proxy class. The same is true for other annotations such as @cache, which is, in plain English, a result of Spring’s proxy mechanism. Therefore, in development, it is best to separate asynchronous services from a class to manage them. The following will focus on.
When can @async Async methods fail?
-
Calling the same class has an @async asynchronous method
: Annotations such as @async, @Transactional, and cache use dynamic proxies in nature. When the Spring container initializes a class object containing AOP annotations, the spring container “replaces” it with a proxy object. It is obvious that the annotation fails because the method is called by the object itself rather than the proxy object, and since it does not go through the Spring container, the solution will follow the same path.
-
The static method is called
-
Call the (private) private method
5. The way to solve problem 1 in 4 (the other 2 and 3 problems can be paid attention to by themselves)
-
The method to be executed asynchronously is extracted into a single class
The principle is that when you extract the asynchronous method into a single class, the class must be managed by Spring and injected when other Spring components need to call it. In this case, the injected class is actually the proxy class.
-
In fact, our injected objects are all member variables assigned to the current Spring component from the Spring container. Since some classes use AOP annotations, it is their proxy objects that actually exist in the Spring container. So we can
Call the asynchronous method by retrieving its own proxy object from the context.
@Controller @RequestMapping("/app"Public class EmailController {// There are several ways to get the ApplicationContext object applicationContext; @RequestMapping(value ="/email/asyncCall", method = GET) @ResponseBody public Map<String, Object> asyncCall() { Map<String, Object> resMap = new HashMap<String, Object>(); TestAsyncTask (); // testAsyncTask(); // Call the asynchronous method EmailController EmailController = by retrieving its own proxy object from the context (EmailController)applicationContext.getBean(EmailController.class); emailController.testAsyncTask(); resMap.put("code", 200); }catch (Exception e) { resMap.put("code", 400); logger.error("error!",e); } returnresMap; } @async public void @async public voidtestAsyncTask() throws InterruptedException { Thread.sleep(10000); System.out.println("Asynchronous task completed!"); }}Copy the code
-
Enable the Cglib proxy and manually obtain the Spring proxy class
To invoke asynchronous methods of the same class.
- First, add to the bootstrap class
@EnableAspectJAutoProxy(exposeProxy = true)
Annotation.
-
Code implementation, as follows:
@Service @Transactional(value = "transactionManager".readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class) public class EmailService { @Autowired private ApplicationContext applicationContext; @Async public void testSyncTask() throws InterruptedException { Thread.sleep(10000); System.out.println("Asynchronous task completed!"); } public void asyncCallTwo() throws InterruptedException { //this.testSyncTask(); // EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class); // emailService.testSyncTask(); boolean isAop = AopUtils.isAopProxy(EmailController.class); // Whether it is a proxy object; boolean isCglib = AopUtils.isCglibProxy(EmailController.class); // Is a CGLIB proxy object; boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class); // whether the JDK dynamic proxy proxy object; // The following is the point!! EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class); EmailService proxy = (EmailService) AopContext.currentProxy(); System.out.println(emailService == proxy ?true : false); proxy.testSyncTask(); System.out.println("end!!!"); }}Copy the code
The difference between asynchronous request and asynchronous call
- First, add to the bootstrap class
- The two use scenarios are different. Asynchronous request is used to solve the pressure caused by concurrent requests to the server, so as to improve the throughput of requests. Asynchronous calls are used to do non-mainline tasks that do not require real-time computation and response, such as synchronizing logs to Kafka for log analysis.
- An asynchronous request waits for a response and needs to return the result to the client. While asynchronous call we often immediately return to the client response, complete the entire request, as for asynchronous call task background slowly run on the line, the client does not care.
Four,
- The use of asynchronous requests and asynchronous calls is basically enough here, and I hope you can point out more problems.
- This article mentioned dynamic proxy, and the implementation principle of SPRING Aop is dynamic proxy, the follow-up will do a detailed interpretation of dynamic proxy, but also hope to support more ha ~
For more technical articles, please pay attention to wechat public number: Java Programmers Gathering Place