Transferred from: Micro reading (www.weidianyuedu.com) Micro reading - complete collection of model articles - a website for free learning knowledge
1, Use of asynchronous requests in Spring Boot**
1. Asynchronous request and synchronous request
Synchronization request
Asynchronous request
Features: you can first release the threads and related resources allocated by the container to the request, reduce the system burden, and release the request of the thread allocated by the container. Its response will be delayed. You can respond to the client when the time-consuming processing is completed (such as long-time operation). Bottom line: it increases the throughput of the server to client requests (we use less in actual production. If there are a large number of concurrent requests, we will load the requests to each node of the cluster service through nginx to share the request pressure. Of course, we can also buffer the requests through the message queue).
2. Implementation of asynchronous request
Method 1: realize asynchronous request in Servlet mode
@RequestMapping(value = "/email/servletReq", method = GET) public void servletReq (HttpServletRequest request, HttpServletResponse response) { AsyncContext asyncContext = request.startAsync(); //Set listener: you can set the callback processing of its start, completion, exception, timeout and other events asyncContext.addListener(new AsyncListener() { @ Override public void onTimeout(AsyncEvent event) throws IOException { System.out.println("timeout...); // Do some related operations after timeout } @ Override public void onStartAsync(AsyncEvent event) throws IOException { System.out.println("thread start"); } @ Override public void onError(AsyncEvent event) throws IOException { System.out.println("error occurred:" + event.getThrowable()); } @ Override public void onComplete(AsyncEvent event) throws IOException { System.out.println("execution completed"); // Here you can do some operations to clean up resources } }); // Set timeout asyncContext.setTimeout(20000); asyncContext.start(new Runnable() { @ 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); } // Asynchronous request completion notification // At this time, the whole request is completed asyncContext.complete(); } }); // At this time and so on The thread connection of request has been released System.out.println("main thread:" + Thread.currentThread().getName()); }
Method 2: it is very simple to use. The directly returned parameters can be wrapped with a layer of label. You can inherit the WebMvcConfigurerAdapter class to set the default thread pool and timeout processing
@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) { //handle callable timeout configurer.setDefaultTimeout(60*1000); configurer.setTaskExecutor(myThreadPoolTaskExecutor); configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor()); } @ Bean public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() { return new TimeoutCallableProcessingInterceptor(); }}
Method 3: similar to method 2, set a timeout callback for WebAsyncTask in the Callable outsourcing layer to realize 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 start:" + Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(4); } catch (Exception e) { // TODO: handle exception } logger.info("returned by the secondary thread"); System.out.println("internal thread return:" + 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; }
Mode 4: DeferredResult can handle some relatively complex business logic. The most important thing is that it can process and return business in another thread, that is, it 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()); //Set timeout DeferredResult<String> result = new DeferredResult<String>(60*1000L); // Processing timeout events Adopt entrustment mechanism result.onTimeout(new Runnable() { @ Override public void run() { System.out.println("DeferredResult timeout"); Result.setresult ("timeout!"); } }); result.onCompletion(new Runnable() { @ Override public void run() { // After completion System.out.println("call completed"); } }); myThreadPoolTaskExecutor.execute(new Runnable() { @ Override public void run() { // Processing business logic System.out.println("internal thread:" + Thread.currentThread().getName()); // Return results result.setResult("DeferredResult!!"); } }); return result; }
2, Use of asynchronous calls in Spring Boot
1. Introduction
Asynchronous request processing. In addition to asynchronous requests, we usually use asynchronous calls. Usually, in the development process, we will encounter a method that has nothing to do with the actual business and is not tight. For example, logging information and other businesses. At this time, it is normal to start a new thread to do some business processing and let the main thread execute other businesses asynchronously.
2. Usage (based on spring)
You need to add @ EnableAsync to the startup class to make the asynchronous call @ Async annotation take effect. You can add this annotation to the methods that need asynchronous execution @ Async ("ThreadPool"), which is a custom thread pool. The code is omitted... Just two tags. Just try it yourself
3. Precautions
By default, when TaskExecutor is not set, the thread pool SimpleAsyncTaskExecutor is used by default, but this thread is not a real thread pool because threads are not reused and a new thread will be created every time they are called. It can be seen from the console log output that the thread name is incremented every time. Therefore, it is best to define a thread by ourselves Program pool. The asynchronous methods called cannot be methods of the same class (including internal classes of the same class) To put it simply, Spring will create a proxy class for it when it starts scanning, and when similar calls are made, it still calls its own proxy class, so it is the same as normal calls. Other annotations such as @ Cache are the same. To put it bluntly, they are made by Spring's agent. Therefore, it is best to extract a separate class for asynchronous services in development Management. The following will focus on..
4. Under what circumstances will @ Async asynchronous methods fail?
Call the same class and bet @ Async asynchronous method:
In spring, annotations such as @ Async, @ Transactional and cache essentially use dynamic proxies. In fact, when the spring container initializes, the spring container will "replace" the class object with AOP annotation as a proxy object (simply understood as this) , the reason why the annotation fails is obvious, because the method is called by the object itself rather than the proxy object, and because it has not passed through the spring container, the solution will follow this idea.
The static method is called
Call the private method
5. How to solve problem 1 in 4 (just pay attention to the other two problems in 2 and 3)
The principle of extracting the methods to be executed asynchronously into a class is that when you extract the methods to be executed asynchronously into a class, the class must be managed by Spring, and other Spring components will be injected when they need to be called. At this time, what is actually injected is the proxy class. In fact, our injection objects are all injected from the Spring container At present, the Spring component assigns member variables. Because some classes use AOP annotations, its proxy object actually exists in the Spring container. Then we can get our own proxy object through the context and call asynchronous methods.
@Controller@RequestMapping("/app")public class EmailController { //There are many ways to obtain ApplicationContext objects. This is the simplest. Let's learn about others by ourselves @ Autowired private ApplicationContext applicationContext; @ RequestMapping(value = "/email/asyncCall", method = GET) @ ResponseBody public Map<String, Object> asyncCall () { Map<String, Object> resMap = new HashMap<String, Object>(); try{ // It doesn't work to call asynchronous methods of the same kind // this.testAsyncTask(); // Get your own proxy object through the context and call asynchronous methods EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class); emailController.testAsyncTask(); resMap.put("code",200); } catch (Exception e) { resMap.put("code",400); logger.error("error!",e); } return resMap; } // Note that it must be public and non static @ Async public void testAsyncTask() throws InterruptedException { Thread.sleep(10000); System.out.println("asynchronous task execution completed!"); }}
Open the cglib proxy and manually obtain the Spring proxy class to call asynchronous methods under the same class. First, add @ EnableAspectJAutoProxy (exposeProxy = true) annotation. The code implementation is 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 execution 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); // Whether it is a proxy object in CGLIB mode; boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class); // Whether it is a proxy object of JDK dynamic proxy mode; // The following is the key!!! 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!!!"); }}
3, The difference between asynchronous request and asynchronous call is different. Asynchronous request is used to solve the pressure on the server caused by concurrent requests, so as to improve the throughput of requests; Asynchronous call is used to do some tasks that are not mainline processes and do not require real-time calculation and response, such as synchronizing logs to kafka for log analysis. The asynchronous request will always wait for the corresponding response, and the result needs to be returned to the client; For asynchronous calls, we often return the response to the client immediately to complete the whole request. As for the task of asynchronous calls, the background can run slowly, and the client won't care.
4, To summarize the use of asynchronous requests and asynchronous calls, it's almost here. I hope you can point out more problems.