Some time ago, I was busy at home and stopped for a long time. I would like to apologize to the friends waiting for the update for not explaining it in advance. I am really sorry for making them wait so long.

Now that we’ve covered the initialization and startup steps of Tomcat, it’s time for the big game! In this article, AS before, I will show you how a Servlet is handled by Tomcat and what the specific processing links are.

I. Request analysis

The description of each component is noted in The Tomcat source Code Study Part 2.

When a servlet request arrives, it first passes through the Connector component, which receives the request.

The component receives the request, encapsulates it and passes it to the Engine component.

The Engine component then locks the corresponding host, context, and Wrapper, and passes them through to find the servlet instance that will eventually process the request.

Second, in-depth exploration

If you remember, in the NioEndpoint class, we had a thread group running above the entry to start Accepter threads, but we didn’t explain what this thread was for

NioEndpoint.startInternal()

If we click to jump to this class, we can see that it implements the Runnable interface, so let’s go straight to its run() method and see its logic.

Poller.run()

As you can see from the comments, this thread is mainly used to poll connected sockets, check if an event is fired, and hand over the associated socket to the corresponding handler when an event occurs. In the source code, we can see that the keyCount variable records the number of pending requests, which can be used to determine later.

Further down the chain, keyCount is used to determine if any requests need to be processed, and if so, selector.selectedKeys() is used to retrieve the set of channels that need to be processed and loop through them. In the while loop we see that all ready channels call the processKey(SK, socketWrapper) method for processing.

Click to jump to the method, here you can see that he did read and write judgment on the SK, since it is a request, it must be a read operation, let’s first read related methods have a look.

NioEndpoint.processKey()

We can see that it first tries to fetch a processing thread from the cache pool. When there are no threads in the cache pool, it creates a new thread and uses it directly if there are any.

AbstractEndpoint.processSocket()

Since it’s a thread, let’s just focus on the core methods of threads. Click the SocketProcessorBase jump to see the Run () method.

SocketProcessorBase.run()

Make a breakpoint at doRun() and click Next to jump to the nioendpoint.dorun () method. The Poller thread is handed over to this thread for processing, where it needs to get the current socket for further processing.

If the socket is not empty, it tries to fetch the processor from connections. If not, it tries to fetch the processor from the processor that has already processed the connection but has not yet destroyed it. Create before you get it. This avoids frequent object creation and destruction.

AbstractProtocol.process()

After obtaining the processor, the process method is called to parse the packet.

SocketEvent () : socketEvent () : socketEvent () : socketEvent ()

AbstractProcessorLight.process()

Here we can see the http11 class, which parses the message and encapsulates the native Request and Response objects. The response here is just a preliminary parameter setting because we haven’t gotten to the return step yet. Then pass into the Adapter for the next step.

Http11Processor.service()

Here we transform the native Request and response to get HttpServletRequest and HttpServletResponse. Then, based on the request information, find the host, context, and Wrapper that can handle the current request.

CoyoteAdapter.service()

In this method you can see that it matches the host,context, and Wrapper that can handle the current request using the getMapper() method. Now, some of you might be wondering, why do I match from a Mapper? I’ll leave you to explore this question and answer it in the next chapter.

CoyoteAdapter.postParseRequest()

If the mapper object is not built, then the mapper object is not built.

Mapper.map()

I don’t know if you remember from part 2, in the StandardService class, the initInternal() and startInternal() methods are initialized and started with the mapperListener method.

The corresponding host, context, and wrapper are found in this method.

Mapper.internalMap()

Evaluste back CoyoteAdapter. PostParseRequest (), we can see the current request corresponding to the host, the context, the wrapper and mapping instances have been found.

All that is left to do is make the call level by level, depending on the link component, until finally the servlet is pulled out and executed.

CoyoteAdapter.service()

Get host first, and proceed to call the next-level component through host

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

Get the context here and continue invoke().

StandardHostValve.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

Once you get the Wrapper, proceed down to get the servlet object from the Wrapper container.

StandardWrapperValve.invoke()

Next, add the resulting servlet to the filter chain (there may be additional processing that is not done directly here) and leave it to call the filter chain for processing.

ApplicationFilterChain.doFilter()

Finally found a concrete example, too not easy!!

ApplicationFilterChain.internalDoFilter()

Third, summary

I collect a large number of computer electronic books, there is a need for small partners to raise oh ~