The basic architecture
Tomcat
It consists of two modules collaboration
connector
container
connector
Responsible for parsing HTTP
the request, for example 请求头
, 查询字符串
, and the like. Generation and later handed over , which is responsible for calling the appropriate .请求参数
HttpRequest
HttpResponse
container
Servlet
Connector
Tomcat
The default Connector
is HttpConnector
. As Connector
we must implement Connector
this interface.
Tomcat
It will open later start a thread , do an infinite loop by ServerSocket
waiting for a request. Once the request is generated Socket
, note that there HttpConnector
will not handle their own Socket
, but give it to HttpProcessor
. Details see below the code , here I only kept the key code.
public void run() { // Loop until we receive a shutdown command while (!stopped) { Socket socket = null; try { socket = serverSocket.accept(); //等待链接 } catch (AccessControlException ace) { log("socket accept security exception", ace); continue; } // Hand this socket off to an appropriate processor HttpProcessor processor = createProcessor(); processor.assign(socket); //这里是立刻返回的 // The processor will recycle itself when it finishes } }
Note that the above processor.assign(socket);
should be returned immediately and does not block there, waiting. Because Tomcat can not only handle one request, it is asynchronous, each processor
process is a separate thread.
HttpProcessor
The above code does not call the show HttpProcessor
the process
way, that this method is how to call it? Let's look at HttpProcessor
the run
method.
public void run() { // Process requests until we receive a shutdown signal while (!stopped) { // Wait for the next socket to be assigned Socket socket = await(); if (socket == null) continue; // Process the request from this socket try { process(socket); } catch (Throwable t) { log("process.invoke", t); } // Finish up this request connector.recycle(this); } }
We found him to call await
a method to block waiting to get socket
method. And before Connector
calling assign
allocated, what is the reason?
Below a closer look await
and assign
approach. These two methods collaboration, when assign
acquired socket
when will be notified await
and then return socket
.
synchronized void assign(Socket socket) { // Wait for the Processor to get the previous Socket while (available) { try { wait(); } catch (InterruptedException e) { } } // Store the newly available Socket and notify our thread this.socket = socket; available = true; notifyAll(); } private synchronized Socket await() { // Wait for the Connector to provide a new Socket while (!available) { try { wait(); } catch (InterruptedException e) { } } // Notify the Connector that we have received this Socket Socket socket = this.socket; available = false; notifyAll(); return (socket); }
The default available
is false
.
Then there is left to do is resolve the request, filling HttpRequest
and HttpResponse
objects, and then to container
be responsible.
Here I go into details, but more how to parse
private void process(Socket socket) { //parse .... connector.getContainer().invoke(request, response); .... }
Container
A Container is an object that can execute requests received from a client, and return responses based on those requests
Container
It is an interface that this class implements the interface instance can be received processing request, the corresponding call Servlet
.
There are four categories Container
, four Container
parallel relationship between not, but the father-son relationship
Engine
- the top-most container, may comprise a plurality ofHost
Host
- represents a virtual host can contain multipleContext
Context
- on behalf of aweb应用
, that isServletContext
, it can contain multipleWrappers
Wrapper
- a representative ofServlet
, can not contain other container, which is the lowest level
Container calls
Is like a container processing plant, processing acceptance request
, processing and pipelining is very similar, but a little different. Here will be used is called a thing, Chinese translation is , to put in order the pipeline processing, the machining tool is called , is like a scalpel, you can add more , finishing tool called Pipeline
管道
request
Valve
Pipeline
Valve
BaseValve
The above list may speak more abstract, let's look at the code. Engine
The top container, so the above invoke
, is performed by Engine
the method. StandardEngine
It is Engine
the default implementation, note that it also implements Pipeline
an interface, and contains Pipeline
.
Its construction method is also specified baseValve
, which is the last call of the pipeline Valve
public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); }
Well, then we see invoke
that this method is inherited from ContainerBase
. Only one line, to between pipeline
, for processing.
public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); }
Here is StandardPipeline
the invoke
realization, which is the default pipeline
implementation.
public void invoke(Request request, Response response) throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request (new StandardPipelineValveContext()).invokeNext(request, response); }
Only one line! Call StandardPipelineValveContext
the invokeNext
method, which is an pipeline
internal class. Let us look
Specific code
public void invokeNext(Request request, Response response) throws IOException, ServletException { int subscript = stage; stage = stage + 1; // Invoke the requested Valve for the current request thread if (subscript < valves.length) { valves[subscript].invoke(request, response, this); //加工 } else if ((subscript == valves.length) && (basic != null)) { basic.invoke(request, response, this); } else { throw new ServletException (sm.getString("standardPipeline.noValve")); } }
It calls pipeline
used Valve
to to request
do the processing, when Valve executed, will call BaseValve
, which is above StandardEngineValve
,
We look at its invoke
methods
// Select the Host to be used for this Request StandardEngine engine = (StandardEngine) getContainer(); Host host = (Host) engine.map(request, true); if (host == null) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost", request.getRequest().getServerName())); return; } // Ask this Host to process this request host.invoke(request, response);
It is through (Host) engine.map(request, true);
the corresponding acquired Host
, and then proceeds to the next layer of containers to continue. Later execution order
And Engine
the same, but many go I
Summary execution order
After a long series of invoke
last execution order of the first layer of the finished container. I guess you see a little dizzy, summary of what I have here.
Connector -> HttpProcessor.process() -> StandardEngine.invoke() -> StandardPipeline.invoke() ->
StandardPipelineValveContext.invokeNext() -> valves.invoke() -> StandardEngineValve.invoke() ->
StandardHost.invoke()
Here to position Engine
this layer end. Next were Host
, exactly the same steps
StandardHost.invoke() -> StandardPipeline.invoke() ->
StandardPipelineValveContext.invokeNext() -> valves.invoke() -> StandardHostValve.invoke() ->
StandardContext.invoke()
And then to Context
this layer processing, select a corresponding to the last Wrapping
executed.
Wrapper
Wrapper
Equivalent to a Servlet
example, StandardContext
will be more in accordance with the request
selected corresponding to the Wrapper
call. We take a look directly
Wrapper
Of basevalve
it is if you call Servlet
the service
method. Here is StandardWrapperValve
the invoke
way I omitted a lot,
Look at the key.
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Allocate a servlet instance to process this request if (!unavailable) { servlet = wrapper.allocate(); } // Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method String jspFile = wrapper.getJspFile(); //是否是jsp if (jspFile != null) sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile); else sreq.removeAttribute(Globals.JSP_FILE_ATTR); if ((servlet != null) && (filterChain != null)) { filterChain.doFilter(sreq, sres); } sreq.removeAttribute(Globals.JSP_FILE_ATTR); }
First call wrapper.allocate()
, this method is very important, it will pass 反射
to find the corresponding servlet
the class
file structure of an example of a return to us. Then create a FilterChain
familiar j2ee
of you should be no stranger to this? This is what we in the development of web app
the use of time filter
. Then on the implementation of doFilter
the method, and it will call internalDoFilter
, we look at this method
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (this.iterator.hasNext()) { ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) iterator.next(); Filter filter = null; filter = filterConfig.getFilter(); filter.doFilter(request, response, this); return; } // We fell off the end of the chain -- call the servlet instance if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { servlet.service((HttpServletRequest) request, (HttpServletResponse) response); } else { servlet.service(request, response); } }
Finally, this approach saw the service
method, now you know in the use of filter
time if you do not perform doFilter
, service
the reason will not be executed by the handle.