How Tomcat Works讀書筆記

首先明確Tomcat的職責是什么,也就是說Tomcat做了哪些事情?
我覺得下面幾件事情是Tomcat必須要做的比較重要的事情。

  1. 監聽某個端口,捕獲HTTP請求。
  2. 將HTTP請求轉換為相應的Request對象,當然也應該需要創建一個Response對象
  3. 然后初始化一個Servlet對象,或者說加載一個Servlet對象,將相應的Request對象和Response對象傳遞進去,之后調用Servlet中的service函數。

Tomcat的架構圖

Tomcat的架構圖.png

上面一張圖片比較詳細,下面放一張簡單一點的架構圖。

imgr.jpg

Tomcat的架構淺析

通過圖片可以看到Tomcat是分成好幾個模塊的。最外面是一個Server,一個Server可以有好幾個Service服務,一個Service服務會有幾個Connector組件,但是只會有一個Container組件,另外還有好幾個不是那么核心的組件,比如說管理Session的組件,寫日志的組件。

其中也提到了最核心的組件(Component)就是ConnectorContainer

Connector的作用就是監聽某一個端口,然后接受HTTP請求,將HTTP請求封裝成Servlet需要使用的HttpServletRequest對象和HttpServletResponse對象。也就是說將純文本的Http請求解析出來了之后變成可以方便使用的Request對象,而Response對象需要作為一個返回值的容納地,也是需要傳遞給Servlet*的。

Container的作用就是根據Request中URL中的信息加載相應的Servlet對象,之后就是調用相應的方法,講結果封裝在Response對象中,最后當然也是轉換為HTTP的文本返回給客戶端。

A Simple Servlet Container

當有了基本的架構概念了之后,我覺得可以直接看一些簡單的小例子,來看一下在代碼中這些組件是如何關聯作用在一起的,下面就把How Tomcat Works中的一個第五章中的小例子貼出來。(其實前面幾個章節有更加簡單的例子,感興趣的可以看下。)

package ex05.pyrmont.startup;

import ex05.pyrmont.core.SimpleContext;
import ex05.pyrmont.core.SimpleContextMapper;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientIPLoggerValve;
import ex05.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.Mapper;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;

public final class Bootstrap2 {
  public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    //繼承了Wrapper,每一個實例里面都是會有一個Servlet的
    Wrapper wrapper1 = new SimpleWrapper();
    //設置Servlet的映射地址
    wrapper1.setName("Primitive");
   //設置Servlet的名字
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");
 
    //context是一個容器可以包含wrapper這個最底層的容器
    Context context = new SimpleContext();
    context.addChild(wrapper1);
    context.addChild(wrapper2);

    Valve valve1 = new HeaderLoggerValve();
    Valve valve2 = new ClientIPLoggerValve();
    //容器中除了其他容器之外還有Valve
    //另外要注意的是每一個context都是實現了Pipeline和Context接口的
    ((Pipeline) context).addValve(valve1);
    ((Pipeline) context).addValve(valve2);
    //這個mapper是做什么的呢?
    Mapper mapper = new SimpleContextMapper();
    mapper.setProtocol("http");
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();
    //容器中還需要加載器,通過反射加載真正的Servlet對象
     context.setLoader(loader);
    // context.addServletMapping(pattern, name);
   //context里面初始化了一個HashMap,存儲映射和Servlet名字
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");
    //因為connector封裝好Reqeust之后會調用容器,所以將容器的聲明給Connector
    connector.setContainer(context);
    try {
      connector.initialize();
       //connector開始監聽端口,要明白底層肯定使用ServerSocket來實現的
      connector.start();

      // make the application wait until we press a key.
      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

BootStrap是一個Tomcat啟動的程序入口。可以看到源代碼中初始化了這么幾個對象,一個HttpConnector,兩個Wrapper,一個Context,兩個Valve,一個Loader等,具體可以看注釋,還是把我的理解寫在注釋里面把。

再看一張圖

host_context_wrapper之間的關系.jpg

從第一張架構圖中可以看到container是分成下面幾個容器的,engine,host,context,wrapper。關系是這樣的一個engine是可以有零個或者多個host的,以此類推。

那么這些容器的作用是什么呢?其實是用來匹配相應的URL,也就是講一個請求的URL來分成者三個部分,最后找到要請求的Servlet。也就是說request是不斷地在容器中傳遞的。其實容器中還有一個叫做Pipeline的東西,暫時還是不是很明白這個東西的作用是什么,每一個Pipeline都有很多的Valve。(有一點是這樣的,Pipeline其實和過濾器是一個很像的東西)

ServletContext在哪里呢?

其實就是StandardContext中的一個全局變量。
Tomcat8.0.37中StandardContext.java部分源碼

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter
    /**
     * The ServletContext implementation associated with this Context.
     */
    protected ApplicationContext context = null;
    /**
     * Return the servlet context for which this Context is a facade.
     */
    @Override
    public ServletContext getServletContext() {

        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());

    }

最后

其實現在如果是用Springmvc這個框架的話,其實只是在web.xml中配置了一個攔截所有請求的前端Servlet,但是之后的事情比如Controller之類的東西都是Sringmvc在做了,而不是最原始的一個請求對應一個Servlet了。

最后的最后

因個人能力有限,以上內容肯定會有錯誤,所有歡迎交流討論,另外如果覺得有幫助,可以點擊一個喜歡,將這篇文章獻給以后的自己和看到最后的你。

參考

JavaWeb學習之Servlet(四)----ServletConfig獲取配置信息、ServletContext的應用

Tomcat容器結構及Pipeline機制 -我們到底能走多遠系列(13)
tomcat架構分析概覽
粗淺看 Tomcat系統架構分析
How do servlets work? Instantiation, sessions, shared variables and multithreading
Tomcat源碼分析之容器整體結構

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容