I was busy at home some time ago, so I stopped for a long time. I would like to apologize to those who are waiting for the update for not explaining in advance. I am really sorry for keeping you waiting for so long.

Now that we’ve covered the initialization and startup steps of Tomcat, it’s time to get to the big picture! In this article, I will take you through the graphic way as before to understand how a Servlet is processed by Tomcat, the specific processing links are what.

Request analysis

Descriptions of each component are noted in the Tomcat Source Study Part 2.

When a servlet request arrives, it first passes through the Connector component, which is used to receive the request.

Once the component receives the request, it wraps the request and passes it to the Engine component.

The Engine component then locks the corresponding Host, Context, and Wrapper layers to find the servlet instance that will ultimately handle the request.

Two, in-depth exploration

In the NioEndpoint class, we had a thread group running above the entrance to the Acceptter thread, but we didn’t explain what that thread was for

NioEndpoint.startInternal()

Click to jump to this class, and we can see that it implements the Runnable interface, so let’s look directly at its run() method to see how it works.

Poller.run()

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

If so, select selector.selectedKeys() to fetch the set of channels that need to be processed and loop through them. In the while loop we see that all the ready channels call the processKey(SK, SocketWrapper) method for processing.

Click to jump over the method, here you can see that he did read and write to the SK judgment, since it is a request to come in, that must be to do the read operation, we advanced read related methods have a look.

NioEndpoint.processKey()

We can see that it first tries to get a processing thread in the cache pool, creates a new thread when there is no thread in the cache pool, and uses it if there is one.

AbstractEndpoint.processSocket()

Since it’s a thread, we care about the core methods of the thread. Click SocketProcessorBase to jump to see the run() method.

SocketProcessorBase.run()

Put 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 the current socket is required for further processing.

After entering this method, we can see that it first checks the wrapper and then pulls out the socket. Then it tries to get the corresponding processor in connections. If not, it tries to get the processor that has already processed the connection, but has not destroyed it. You can’t get it before creating it. This avoids frequent object creation and destruction.

AbstractProtocol.process()

After the processor is obtained, the process method is called to parse the message.

After entering this method, we can see that this is a judgment of the state of SocketEvent. Our current request is mainly read state.

AbstractProcessorLight.process()

Here we can see that we are entering the HTTP11 class, where we parse the packet and encapsulate the native Request and Response objects. The response here is just a preliminary parameter setting since we haven’t reached the return step. The next step is to pass the Adapter in for the next step.

Http11Processor.service()

Here you convert the native request and response to get HttpServletRequest and HttpServletResponse. We then use the request information to find the host, context, or wrapper that can handle the current request.

CoyoteAdapter.service()

You can see in this method that it matches the host,context, and wrapper that can handle the current request through the getMapper() method. Some of you may wonder, why do you match from the mapper? I’ll leave that to you to explore, and I’ll answer that in the next part.

CoyoteAdapter.postParseRequest()

In the previous method, we obtained the Mapper object after getting the service through the Connector, but we did not see the construction of the Mapper object after coming in. Where did the Mapper object come from?

Mapper.map()

If you remember from the second article, in the StandardService class, initInternal() and startInternal(), we had the MapPerListener method initialized and started.

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.

The next step is to make successive calls based on the link component, until finally the servlet is pulled out for execution.

CoyoteAdapter.service()

Get the host first, and then call the next component through the host

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

So I’m going to get the context here, and I’m going to invoke().

StandardHostValve.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

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

StandardWrapperValve.invoke()

Then, the resulting servlet is added to the filter chain (there may be other processing, not directly processed here), leaving the filter chain to be called and processed uniformly.

ApplicationFilterChain.doFilter()

Finally found the concrete instance, too not easy!!

ApplicationFilterChain.internalDoFilter()

Third, summary

I have a large collection
Computer electronic bookIf you need it, ask yourself