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

How the container in Tomcat handles the request

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

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

This article will explain in detail how containers in Tomcat handle requests. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.

Start with Adapter

Let's continue to analyze the Adapter source code of the previous article. The source code at the end of the last article is as follows:

/ / Source code 1. Class: CoyoteAdapter implements Adapterpublic void service (org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {Request request = (Request) req.getNote (ADAPTER_NOTES); Response response = (Response) res.getNote (ADAPTER_NOTES); postParseSuccess = postParseRequest (req, request, res, response) If (postParseSuccess) {/ / check valves if we support async request.setAsyncSupported (connector.getService (). GetContainer (). GetPipeline (). IsAsyncSupported ()); / / Calling the container connector.getService (). GetContainer (). GetPipeline (). GetFirst (). Invoke (request, response) }}

The main function of the above source code is to get the container, then call getPipeline () to get the Pipeline, and finally go to the invoke call. Let's see what the Pipeline does.

/ / Source 2.Pipeline interface public interface Pipeline extends Contained {public Valve getBasic (); public void setBasic (Valve valve); public void addValve (Valve valve); public Valve [] getValves (); public void removeValve (Valve valve); public Valve getFirst (); public boolean isAsyncSupported (); public void findNonAsyncValves (Set result);} / / Source code 3. Valve interface public interface Valve {public Valve getNext (); public void setNext (Valve valve); public void backgroundProcess () Public void invoke (Request request, Response response) throws IOException, ServletException; public boolean isAsyncSupported ()

We can literally understand that Pipeline is a pipe, and Valve is a valve, in fact, the role in Tomcat is similar to the literal meaning. Each container has a pipe, and there are multiple valves in the pipe. We prove this point through the following analysis.

Pipe-Valve (Pipeline-Valve)

We see that the above source code is the interface between Pipeline and Valve, Pipeline is mainly to set Valve, and Valve is a linked list, and then you can call the invoke method. Let's review this source code:

/ / source code 4connector.getService () .getContainer () .getPipeline () .getFirst () .invoke (request, response)

Here is the pipe that gets the container directly, and then gets the first Valve to call. We mentioned earlier that Valve is a linked list, and only the first one can be called here, that is, the last one can be called through Next. As mentioned in our first article, "how Tomcat starts in SpringBoot," containers are divided into four child containers, Engine, Host, Context, and Wrapper, which are also parent-child relationships, Engine > Host > Context > Wrapper.

As I mentioned earlier, each container has a Pipeline, so how does this show up? If we look at the source code of the container interface, we can see that Pipeline is a basic attribute defined by the container interface:

/ / Source code 5.public interface Container extends Lifecycle {/ / omit other codes / * Return the Pipeline object that manages the Valves associated with * this Container. * * @ return The Pipeline * / public Pipeline getPipeline ();}

We know that each container has a Pipeline, there are many valves (Valve) in the pipe, and Valve can be chained, so the question is, how does the Valve in the parent container pipe call the Valve in the child container? In the implementation class StandardPipeline of Pipeline, we found the following source code:

/ * * / Source code 6. * The basic Valve (if any) associated with this Pipeline. * / protected Valve basic = null; / * * The first valve associated with this Pipeline. * / protected Valve first = null; public void addValve (Valve valve) {/ / omit some code / / Add this Valve to the set associated with this Pipeline if (first = = null) {first = valve; valve.setNext (basic);} else {Valve current = first While (current! = null) {/ / set Valve here to ensure that the last one is basic if (current.getNext () = = basic) {current.setNext (valve); valve.setNext (basic); break;} current = current.getNext () } container.fireContainerEvent (Container.ADD_VALVE_EVENT, valve);}

According to the above code, we know that basic is the last valve in a Pipeline. According to reason, as long as the last valve is the first valve of the next container, all chain calls can be completed. Let's use a request debug to see if it is the same as our guess. We hit a breakpoint in the service method in CoyoteAdapter, and the effect is as follows:

Here we know that when the adapter invokes the container, that is, the pipe that invokes Engine, there is only one valve, basic, with a value of StandardEngineValve. We found that the invoke method for this valve is as follows:

/ / Source code 7.public final void invoke (Request request, Response response) throws IOException, ServletException {/ / Select the Host to be used for this Request Host host = request.getHost (); if (host = = null) {/ / HTTP 0.9 or HTTP 1.0 request without a host when no default host / / is defined. This is handled by the CoyoteAdapter. Return;} if (request.isAsyncSupported ()) {request.setAsyncSupported (host.getPipeline (). IsAsyncSupported ());} / / Ask this Host to process this request host.getPipeline (). GetFirst (). Invoke (request, response);}

Let's continue with debug to view the results as follows:

So the basic here will actually call the Pipeline and Valve of the Host container, that is, the basic in each container pipe is responsible for invoking the valve of the next child container. I use a picture to show:

This diagram clearly describes how the container within the Tomcat flows the request. The request from the Connector enters the Engine container, the Engine makes chain calls through the valve (Valve) in the Pieline, and the last basic valve is responsible for invoking the first valve of the next container, all the way to Wrapper, and then Wrapper executes Servlet.

Let's take a look at the Wrapper source code to see if it's really what we said:

/ / Source code 8. Public final void invoke (Request request, Response response) throws IOException, ServletException {/ / omit partial source code Servlet servlet = null; if (! unavailable) {servlet = wrapper.allocate ();} / / Create the filter chain for this request ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain (request, wrapper, servlet) FilterChain.doFilter (request.getRequest (), response.getResponse ());}

When you see here, you might say that you only created the filter (Filter) and called it, and did not call Servlet. Yes, there is no call to Servlet, but we know that the filter (Filter) is executed before Servlet, that is to say, Servlet will be executed after filterChain.doFilter execution. Let's see if the source code of ApplicationFilterChain is what we said:

/ / Source code 9. Public void doFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException {/ / omit some code internalDoFilter (request,response) } / / Source code 10. Private void internalDoFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException {/ / omit partial code / / Call the next filter if there is one if (pos < n) {/ / omit partial code ApplicationFilterConfig filterConfig = filters [pos++]; Filter filter = filterConfig.getFilter () Filter.doFilter (request, response, this); return;} / / call servlet / / We fell off the end of the chain-- call the servlet instance servlet.service (request, response)

Through the source code, we find that after calling all the filters (Filter), servlet starts to call service. Let's look at the implementation class of servlet

HttpServlet and GenericServlet, which we are familiar with here, are the classes of the Tomcat package, but there is actually only HttpServlet, because GenericServlet is the parent class of HttpServlet. After that, it is handed over to the framework for processing, and the request within the Tomcat has been completed.

Implementation of Multi-application isolation in Tomcat

We know that Tomcat supports the deployment of multiple applications, so how does Tomcat support the deployment of multiple applications? How do you ensure that there is no confusion between multiple applications? To understand this, let's go back to the adapter, back to the service method.

/ / Source code 11. Class: CoyoteAdapterpublic void service (org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {/ / omit some code / / Parse and set Catalina and configuration specific / / request parameters / / handle URL mapping postParseSuccess = postParseRequest (req, request, res, response) If (postParseSuccess) {/ / check valves if we support async request.setAsyncSupported (connector.getService (). GetContainer (). GetPipeline (). IsAsyncSupported ()); / / Calling the container connector.getService (). GetContainer (). GetPipeline (). GetFirst (). Invoke (request, response) }}

In the previous source code, we only talked about connector.getService (). GetContainer (). GetPipeline (). GetFirst (). Invoke (request, response). This part of the code is the calling container, but before calling the container, there is a postParseRequest method used to handle the mapping request. Let's follow up and take a look at the source code:

/ / Source code 12. Class: CoyoteAdapter protected boolean postParseRequest (org.apache.coyote.Request req, Request request, org.apache.coyote.Response res, Response response) throws IOException, ServletException {omit part of the code boolean mapRequired = true; while (mapRequired) {/ / This will map the the latest version by default connector.getService (). GetMapper (). Map (serverName, decodedURI, version, request.getMappingData ()) / / A 404 error if (request.getContext () = = null) {/ / Don't overwrite an existing error if (! response.isError ()) {response.sendError (404, "Not found");} / / Allow processing to continue is reported without finding the context. / / If present, the error reporting valve will provide a response / / body. Return true;}}

Here is the loop to process the Url mapping. If the Context is not found, a 404 error will be returned. Let's continue to look at the source code:

/ / Source code 13. Class: Mapperpublic void map (MessageBytes host, MessageBytes uri, String version, MappingData mappingData) throws IOException {if (host.isNull ()) {String defaultHostName = this.defaultHostName; if (defaultHostName = = null) {return;} host.getCharChunk (). Append (defaultHostName);} host.toChars (); uri.toChars () InternalMap (host.getCharChunk (), uri.getCharChunk (), version, mappingData);} / / source code 14. Class: Mapper private final void internalMap (CharChunk host, CharChunk uri, String version, MappingData mappingData) throws IOException {/ / omit some code / / Virtual host mapping handles Host mapping MappedHost [] hosts = this.hosts; MappedHost mappedHost = exactFindIgnoreCase (hosts, host); / / omit part of the code if (mappedHost = = null) {mappedHost = defaultHost If (mappedHost = = null) {return;}} mappingData.host = mappedHost.object; / / Context mapping processing context mapping ContextList contextList = mappedHost.contextList; MappedContext [] contexts = contextList.contexts; / / omit part of the code if (context = = null) {return } mappingData.context = contextVersion.object; mappingData.contextSlashCount = contextVersion.slashCount; / / Wrapper mapping handles Servlet mapping if (! contextVersion.isPaused ()) {internalMapWrapper (contextVersion, uri, mappingData);}}

As the above source code is more, I omitted a lot of code, retained to understand the main logic of the code, generally speaking, dealing with Url consists of three parts, mapping Host, mapping Context and mapping Servlet (in order to save space, specific details of the source code please interested students to study).

One detail we can find here is that the three processing logics are closely related, and Context will be processed only if Host is not empty, and the same is true for Servlet. So here, as long as the Host configuration is different, then all the subsequent sub-containers are different, and the effect of application isolation is completed. But for SpringBoot embedded Tomcat mode (started with jar package), there is no pattern to implement multiple applications, and an application itself is a Tomcat.

To make it easier to understand, I also drew a diagram of multi-application isolation. Here we assume that there are two domain names admin.luozhou.com and web.luozhou.com, and then I deploy two applications under each domain name, namely User,log,blog,shop. Then when I want to add users, I will request the Servlet of add under the Context of User under the domain name of admin.luozhou.com (note: the design of the example here does not conform to the actual development principle, and the granularity of add should be the completion of controller in the framework, not Servlet).

Summary

In this article, we looked at how containers handle requests in Tomcat. Let's review the content:

The connector throws the request to the adapter and invokes the container (Engine)

The interior of the container is called through the Pieline-Valve mode, and the parent invocation of the child container is mainly done through a basic valve.

After the last child container wrapper completes the call, it builds a filter to make the filter call, and after the call is completed, it goes to the last step inside the Tomcat and calls servlet. You can also understand our commonly used HttpServlet, where all frameworks based on the Servlet specification enter the framework process (including SpringBoot).

Finally, we also analyze how Tomcat implements multi-application isolation. Through the analysis of multi-application isolation, we also understand why Tomcat designs so many sub-containers, and multi-sub-containers can complete different granularity isolation levels to achieve different scenario requirements.

This is the end of the article on "how containers in Tomcat handle requests". I hope the above content can be helpful to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

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

Internet Technology

Wechat

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

12
Report