Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The method of realizing Asynchronous Servlet with Tomcat

2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces the method of Tomcat to achieve asynchronous Servlet, the article introduces in great detail, has a certain reference value, interested friends must read it!

Hand-play an asynchronous Servlet.

We implement a Servlet directly through the SpringBoot framework, and only the Servlet code is shown here:

@ WebServlet (urlPatterns = "/ async", asyncSupported = true) @ Slf4jpublic class AsyncServlet extends HttpServlet {ExecutorService executorService = Executors.newSingleThreadExecutor (); @ Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/ / enable asynchronous context final AsyncContext ctx = req.startAsync (); / / submit thread pool asynchronous execution executorService.execute (new Runnable () {@ Override public void run () {try {log.info ("async Service is ready to execute") / / simulate the time-consuming task Thread.sleep (10000L); ctx.getResponse (). GetWriter (). Print ("async servlet"); log.info ("async Service executed");} catch (IOException e) {e.printStackTrace ();} catch (InterruptedException e) {e.printStackTrace ();} / the callback is completed after the final execution. Ctx.complete ();});}

The above code implements an asynchronous Servlet and implements the doGet method. Note that in SpringBoot, you need to restart the class plus the @ ServletComponentScan annotation to scan the Servlet. Now that the code is written, let's take a look at how it actually works.

After we send a request, we see a response from the page, and at the same time, we see that the request time has taken 10.05s, so our Servlet can work properly. Some students will surely ask, isn't this asynchronous servlet? Your response time is not speeding up. What's the use? Yes, our response time can not be accelerated, it still depends on our business logic, but after our asynchronous servlet request, depending on the asynchronous execution of the business, we can return immediately, that is to say, the thread of Tomcat can be recycled immediately. By default, the core thread of Tomcat is 10, and the maximum number of threads is 200. we can recover threads in time, which means we can handle more requests. Can increase our throughput, which is also the main role of asynchronous Servlet.

The Internal principle of Asynchronous Servlet

After understanding the role of asynchronous Servlet, let's take a look at how Tomcat first asynchronously Servlet. In fact, the main core logic of the above code is two parts, final AsyncContext ctx = req.startAsync (); and ctx.complete (); so let's see what they have done.

Public AsyncContext startAsync (ServletRequest request, ServletResponse response) {if (! isAsyncSupported ()) {IllegalStateException ise = new IllegalStateException (sm.getString ("request.asyncNotSupported")); log.warn (sm.getString ("coyoteRequest.noAsync", StringUtils.join (getNonAsyncClassNames ()), ise); throw ise;} if (asyncContext = = null) {asyncContext = new AsyncContextImpl (this);} asyncContext.setStarted (getContext (), request, response, request==getRequest () & response==getResponse (). GetResponse ()) AsyncContext.setTimeout (getConnector (). GetAsyncTimeout ()); return asyncContext;}

We found that req.startAsync (); just saved an asynchronous context and set some basic information, such as Timeout, by the way, the default timeout set here is 30s, which means that when your asynchronous processing logic exceeds 30s, an error will be reported, and ctx.complete () will be executed at this time; an IllegalStateException exception will be thrown.

Let's look at the logic of ctx.complete ();

Public void complete () {if (log.isDebugEnabled ()) {logDebug ("complete");} check (); request.getCoyoteRequest (). Action (ActionCode.ASYNC_COMPLETE, null);} / / Class: AbstractProcessor public final void action (ActionCode actionCode, Object param) {case ASYNC_COMPLETE: {clearDispatches (); if (asyncStateMachine.asyncComplete ()) {processSocketEvent (SocketEvent.OPEN_READ, true);} break } / / Class: AbstractProcessor protected void processSocketEvent (SocketEvent event, boolean dispatch) {SocketWrapperBase socketWrapper = getSocketWrapper (); if (socketWrapper! = null) {socketWrapper.processSocket (event, dispatch);} / Class: AbstractEndpointpublic boolean processSocket (SocketWrapperBase socketWrapper, SocketEvent event, boolean dispatch) {/ / omit part of the code SocketProcessorBase sc = null; if (processorCache! = null) {sc = processorCache.pop () } if (sc = = null) {sc = createSocketProcessor (socketWrapper, event);} else {sc.reset (socketWrapper, event);} Executor executor = getExecutor (); if (dispatch & & executor! = null) {executor.execute (sc);} else {sc.run ();} return true;}

Therefore, the processSocket method of AbstractEndpoint will eventually be called here. Students who have read my previous blog should have the impression that EndPoint is used to accept and process requests, and then it will be handed over to Processor for protocol processing.

Class: AbstractProcessorLightpublic SocketState process (SocketWrapperBase socketWrapper, SocketEvent status) throws IOException {/ / omitted part diam SocketState state = SocketState.CLOSED; Iterator dispatches = null; do {if (dispatches! = null) {DispatchType nextDispatch = dispatches.next (); state = dispatch (nextDispatch.getSocketStatus ());} else if (status = = SocketEvent.DISCONNECT) {} else if (isAsync () | isUpgrade () | state = SocketState.ASYNC_END) {state = dispatch (status) If (state = = SocketState.OPEN) {state = service (socketWrapper);} else if (status = = SocketEvent.OPEN_WRITE) {state = SocketState.LONG;} else if (status = = SocketEvent.OPEN_READ) {state = service (socketWrapper);} else {state = SocketState.CLOSED;}} while (state = = SocketState.ASYNC_END | dispatches! = null & & state! = SocketState.CLOSED); return state;}

This part is the key point. AbstractProcessorLight will judge whether to call service (socketWrapper) according to the state of SocketEvent. The method will eventually be called to the container to complete the call of business logic. Our request is called after execution, so it must not go into the container, otherwise it will be an endless loop. Here, through isAsync () judgment, we will enter dispatch (status) and finally call the asyncDispatch method of CoyoteAdapter.

Public boolean asyncDispatch (org.apache.coyote.Request req, org.apache.coyote.Response res, SocketEvent status) throws Exception {/ / omit some codes Request request = (Request) req.getNote (ADAPTER_NOTES); Response response = (Response) res.getNote (ADAPTER_NOTES); boolean success = true; AsyncContextImpl asyncConImpl = request.getAsyncContextInternal (); try {if (! request.isAsync ()) {response.setSuspended (false) } if (status==SocketEvent.TIMEOUT) {if (! asyncConImpl.timeout ()) {asyncConImpl.setErrorState (null, false);}} else if (status==SocketEvent.ERROR) {} if (! request.isAsyncDispatching () & & request.isAsync ()) {WriteListener writeListener = res.getWriteListener (); ReadListener readListener = req.getReadListener (); if (writeListener! = null & & status== SocketEvent.OPEN_WRITE) {ClassLoader oldCL = null Try {oldCL = request.getContext (). Bind (false, null); res.onWritePossible (); / / execute browser response here and write data if (request.isFinished () & & req.sendAllDataReadEvent () & & readListener! = null) {readListener.onAllDataRead ();}} catch (Throwable t) {} finally {request.getContext (). Unbind (false, oldCL) It is determined that asynchrony is in progress, which means that this is not a callback for completing the method, but a normal asynchronous request. Continue to call the container. If (request.isAsyncDispatching ()) {connector.getService (). GetContainer (). GetPipeline (). GetFirst (). Invoke (request, response); Throwable t = (Throwable) request.getAttribute (RequestDispatcher.ERROR_EXCEPTION); if (t! = null) {asyncConImpl.setErrorState (t, true) }} / / Note here, if there is a timeout or an error, request.isAsync () will return false in order to output the error to the client as soon as possible. If (! request.isAsync ()) {/ / here is also the output logic request.finishRequest (); response.finishResponse ();} / destroy request and response if (! success | |! request.isAsync () {updateWrapperErrorCount (request, response); request.recycle (); response.recycle ();}} return success;}

The above code is that ctx.complete () executes the final method (omitting a lot of details, of course), completes the output of the data, and finally outputs it to the browser.

Some students here may say, I know that after asynchronous execution, the call ctx.complete () will be output to the browser, but how does Tomcat know that it does not have to return to the client after the first doGet request is completed? The key code is the service method in CoyoteAdapter. Part of the code is as follows:

PostParseSuccess = postParseRequest (req, request, res, response); / / omit part of the code if (postParseSuccess) {request.setAsyncSupported (connector.getService (). GetContainer (). GetPipeline (). IsAsyncSupported ()); connector.getService (). GetContainer (). GetPipeline (). GetFirst (). Invoke (request, response);} if (request.isAsync ()) {async = true } else {/ / output data to client request.finishRequest (); response.finishResponse (); if (! async) {updateWrapperErrorCount (request, response); / / destroy request and response request.recycle (); response.recycle ();}

After calling Servlet, this part of the code uses request.isAsync () to determine whether it is an asynchronous request, and if it is an asynchronous request, set async = true. If it is a non-asynchronous request, the output data to the client logic is executed, while the request and response are destroyed. This completes the operation of not responding to the client after the end of the request.

Why the @ EnableAsync annotation of Spring Boot is not asynchronous Servlet

Because when I was preparing to write this article, I queried a lot of data and found that writing SpringBoot asynchronous programming depends on the @ EnableAsync annotation, and then use multithreading to complete the business logic in Controller, and finally summarize the results and return the output. Here take a Nuggets boss's article as an example, "SpringBoot asynchronous programming guide that beginners can understand". This article is very easy to understand, very good, from the business level, it is indeed asynchronous programming, but there is a problem, apart from the parallel processing of the business, it is not asynchronous for the whole request, that is to say, it can not immediately release Tomcat threads, thus can not achieve the effect of asynchronous Servlet. Here I also wrote a demo with reference to the above, and let's verify why it is not asynchronous.

@ RestController@Slf4jpublic class TestController {@ Autowired private TestService service; @ GetMapping ("/ hello") public String test () {try {log.info ("testAsynch Start"); CompletableFuture test1= service.test1 (); CompletableFuture test2= service.test2 (); CompletableFuture test3 = service.test3 (); CompletableFuture.allOf (test1, test2, test3); log.info ("test1=" + test1.get ()); log.info ("test2=" + test2.get ()) Log.info ("test3=" + test3.get ());} catch (InterruptedException e) {e.printStackTrace ();} catch (ExecutionException e) {e.printStackTrace ();} return "hello";} @ Servicepublic class TestService {@ Async ("asyncExecutor") public CompletableFuture test1 () throws InterruptedException {Thread.sleep (3000L); return CompletableFuture.completedFuture ("test1");} @ Async ("asyncExecutor") public CompletableFuture test2 () throws InterruptedException {Thread.sleep (3000L) Return CompletableFuture.completedFuture ("test2");} @ Async ("asyncExecutor") public CompletableFuture test3 () throws InterruptedException {Thread.sleep (3000L); return CompletableFuture.completedFuture ("test3");}} @ SpringBootApplication@EnableAsyncpublic class TomcatdebugApplication {public static void main (String [] args) {SpringApplication.run (TomcatdebugApplication.class, args);} @ Bean (name = "asyncExecutor") public Executor asyncExecutor () {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor (); executor.setCorePoolSize (3); executor.setMaxPoolSize (3) Executor.setQueueCapacity; executor.setThreadNamePrefix ("AsynchThread-"); executor.initialize (); return executor;}

Let me run it here to see the effect.

Here, after my request, I hit a breakpoint before calling the container to execute the business logic, and then hit a breakpoint again after the return. After the Controller execution, the request is returned to the CoyoteAdapter, and the request () is judged to be false. According to the figure, it is false, then request.finishRequest () and response.finishResponse () are executed to execute the end of the response and destroy the request and response body. Interestingly, when I was experimenting, I found that the response body had already appeared on the page of the browser before executing request.isAsync (), which was already output by the SpringBoot framework through the writeInternal method in the StringHttpMessageConverter class.

The core logic of the above analysis is that after the Tomcat thread executes the CoyoteAdapter call container, it must wait for the request to return, and then determine whether it is an asynchronous request, and then process the request, and then complete the execution before the thread can be reclaimed. My initial asynchronous Servlet example will return immediately after executing the doGet method, that is, it will go directly to the logic of request.isAsync (), and then the logic of the entire thread will be completed and the thread will be recycled.

Talk about the usage scenario of asynchronous Servlet

After analyzing so much, what are the usage scenarios of asynchronous Servlet? In fact, we only need to grasp one point to analyze, that is, asynchronous Servlet improves the throughput of the system and can accept more requests. Suppose that there are not enough threads in Tomcat in the web system, and a large number of requests are waiting, and the optimization at the application level of the Web system can no longer be optimized, that is, the response time of business logic cannot be shortened. At this time, if you want to reduce the waiting time of users and improve throughput, you can try using asynchronous Servlet.

Let's take a practical example: for example, when we make a short message system, the real-time requirement is very high, so the waiting time is as short as possible, and we actually entrust the operator to send the sending function, that is to say, we have to call the interface, assuming that the concurrency is very high, then the business system calls our SMS function at this time, and it is possible to use up our Tomcat thread pool. The rest of the requests will wait in the queue, and at this time, the delay of SMS messages will go up. In order to solve this problem, we can introduce asynchronous Servlet to accept more SMS requests, thus reducing the delay of SMS messages.

These are all the contents of this article entitled "Tomcat's method of implementing Asynchronous Servlet". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report