Tomcat對HTTP請求的處理(一)


摘要:本文主要介紹了tomcat在啟動過程中為了處理HTTP請求之前所做的准備工作

在之前的文章中介紹了tomcat的啟動,關閉過程,后續又陸陸續續介紹了tomcat內部很多機制以及機制的源碼。大家都知道在tomcat啟動完畢以后就可以對外提供服務了,如果訪問 http://localhost:8080 就可以訪問tomcat的主頁,那么我們今天就來查看下tomcat是如何處理這個請求的,也就是如何處理http請求的。

之前我們查看了tomcat啟動的時候的部分源碼(http://www.cnblogs.com/coldridgeValley/p/5631610.html),有的地方沒有深入去看。現在我們來重點看下tomcat在啟動的過程中如何初始化接收請求的各種組件。

Connector構造函數初始化

從tomcat的架構中我們知道,Connector組件是用來接收外部請求的,那么我們就從Connector的初始化來說起。

我們之前的文章說過在啟動的過程中會調用Catalina類的load()方法,在load()方法中有解析server.xml的代碼如下:

//代碼段1
// Create and execute our Digester
Digester digester = createStartDigester();

createStartDigester()方法中關於Connector的具體代碼如下:

 //代碼段2
 digester.addRule("Server/Service/Connector", new ConnectorCreateRule());

具體的規則就在ConnectorCreateRule類中:

//代碼段3
@Override
public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
    Service svc = (Service)digester.peek();
    Executor ex = null;
    if ( attributes.getValue("executor")!=null ) {
        ex = svc.getExecutor(attributes.getValue("executor"));
    }
	//11111
    Connector con = new Connector(attributes.getValue("protocol"));
    if ( ex != null )  _setExecutor(con,ex);
    
    digester.push(con);
}

在標注1的地方attributes.getValue("protocol")指的就是server.xml<Connector>標簽配置的屬性:

	//代碼段4
    <Connector minSpareThreads="10"  maxThreads="200" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

所以其實這里傳遞的是HTTP/1.1這個字符串。我們繼續查看new Connector()源碼。

//代碼段5
public Connector(String protocol) {
	//1111
	setProtocol(protocol);
	// Instantiate protocol handler
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
		//222  初始化protocolHanler變量
        this.protocolHandler = (ProtocolHandler) clazz.newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
}

查看標注1的 setProtocol()方法源碼:

//代碼段6
public void setProtocol(String protocol) {

    if (AprLifecycleListener.isAprAvailable()) {
        if ("HTTP/1.1".equals(protocol)) {
            setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11AprProtocol");
        } else if ("AJP/1.3".equals(protocol)) {
            setProtocolHandlerClassName
                ("org.apache.coyote.ajp.AjpAprProtocol");
        } else if (protocol != null) {
            setProtocolHandlerClassName(protocol);
        } else {
            setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11AprProtocol");
        }
    } else {
		//1111111111
        if ("HTTP/1.1".equals(protocol)) {
            setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11Protocol");
        } else if ("AJP/1.3".equals(protocol)) {
            setProtocolHandlerClassName
                ("org.apache.coyote.ajp.AjpProtocol");
        } else if (protocol != null) {
            setProtocolHandlerClassName(protocol);
        }
    }

}

public void setProtocolHandlerClassName(String protocolHandlerClassName) {
    this.protocolHandlerClassName = protocolHandlerClassName;
}

默認走的是標注1 的地方,所以最后就是把Connector類的protocolHandlerClassName成員變量賦值為org.apache.coyote.http11.Http11Protocol

繼續查看Connector構造方法標注2的地方

//代碼段7
this.protocolHandler = (ProtocolHandler) clazz.newInstance();

也就是說Connector的初始化構造函數中把protocolHandler這個變量初始化為Http11Protocol的一個實例。(僅針對http請求)

Connector的init()方法初始化

我們在tomcat生命周期管理的文章中部分講解過關於Connectorinit()方法,今天我們重新完整查看下:

//代碼段8
@Override
protected void initInternal() throws LifecycleException {
	//111 調用父類 注冊到jmx
    super.initInternal();

    // Initialize adapter
	//222 新建 CoyoteAdapter對象 將新建對象設置到protocolHandler對象中
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    // Make sure parseBodyMethodsSet has a default
    if( null == parseBodyMethodsSet ) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() &&
            !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerNoApr",
                        getProtocolHandlerClassName()));
    }

    try {
		//333 調用protocalHander的 init()方法
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException
            (sm.getString
             ("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }

    // Initialize mapper listener
	//4444  調用mapperListener的init() 方法
    mapperListener.init();
}

代碼邏輯很簡單,稍微梳理下。

  1. 注冊到jmx
  2. 新建CoyoteAdapter對象,設置到protocalHandler中。(重要 以后會提到)
  3. 調用protocalHandler對象的init()方法
  4. 調用mapperListenerinit()方法 (沒啥好看的,就是注冊到jmx,至於這個對象是干嘛的,后面會看到)

protocalHandlerHttp11Protocol的實例,查看其init()方法之前先看下Http11Protocol的繼承體系方便我們查看:

最先在AbstractHttp11JsseProtocol找到了init方法:

//代碼段9
@Override
public void init() throws Exception {
    // SSL implementation needs to be in place before end point is
    // initialized
    sslImplementation = SSLImplementation.getInstance(sslImplementationName);
    super.init();
}

調用了super.init(),所以繼續往上找,在AbstractProtocol中找到init()方法:

//代碼段10
@Override
public void init() throws Exception {
   	//略代碼
    try {
		//調用endpoint.init()方法
        endpoint.init();
    } catch (Exception ex) {
        getLog().error(sm.getString("abstractProtocolHandler.initError",
                getName()), ex);
        throw ex;
    }
}

可以看到調用了endpoint變量的init()方法,而endpoint變量的初始化是在代碼段5 初始化變量protocolHandler的時候進行的。

//代碼段11
//調用newInstance方法等於調用該類的無參構造函數
this.protocolHandler = (ProtocolHandler) clazz.newInstance();

public Http11Protocol() {
    endpoint = new JIoEndpoint();
    cHandler = new Http11ConnectionHandler(this);
    ((JIoEndpoint) endpoint).setHandler(cHandler);
    setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
    setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}

所以我們繼續查看JIoEndpoint類的init()方法,最后在其父類AbstractEndpoint中找到init()方法:

//代碼段12
public final void init() throws Exception {
    testServerCipherSuitesOrderSupport();
    if (bindOnInit) {
		//調用bind()方法,但是因為AbstractEndpoint類的`bind()`方法為抽象方法,所以這個留給子類實現,繼續去類JIoEndpoint中查找   (模版設計模式)
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
}

我們繼續在JIOEndpoint類中查找bind()方法:

//代碼段13
  @Override
public void bind() throws Exception {

    // Initialize thread count defaults for acceptor
	//初始化 connector 默認起始鏈接數
    if (acceptorThreadCount == 0) {
		//如果不配置,默認是0 初始化為1
        acceptorThreadCount = 1;
    }
    // Initialize maxConnections
	// 初始化Connector的最大鏈接數
    if (getMaxConnections() == 0) {
        // User hasn't set a value - use the default
        setMaxConnections(getMaxThreadsExecutor(true));
    }
	// 初始化 serverSocketFactory 用來處理 socket
    if (serverSocketFactory == null) {
        if (isSSLEnabled()) {
            serverSocketFactory =
                handler.getSslImplementation().getServerSocketFactory(this);
        } else {
            serverSocketFactory = new DefaultServerSocketFactory(this);
        }
    }

    if (serverSocket == null) {
        try {
            if (getAddress() == null) {
				//初始化一個默認跟這個JIOEndpoint管理的socket (工廠設計模式)
                serverSocket = serverSocketFactory.createSocket(getPort(),
                        getBacklog());
            } else {
                serverSocket = serverSocketFactory.createSocket(getPort(),
                        getBacklog(), getAddress());
            }
        } catch (BindException orig) {
          //異常處理 略
        }
    }

}

到此就是代碼段8protocolHandlerinit()方法調用完畢,Connector類的init()方法調用完畢。

Connector的start()方法初始化

tomcat啟動的時候在調用init()方法以后,會繼續調用start()方法,繼續查看,Connectorstart()方法。

//代碼段14
@Override
protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getPort() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
    }

    setState(LifecycleState.STARTING);

    try {
		//11111
        protocolHandler.start();
    } catch (Exception e) {
        String errPrefix = "";
        if(this.service != null) {
            errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
        }

        throw new LifecycleException
            (errPrefix + " " + sm.getString
             ("coyoteConnector.protocolHandlerStartFailed"), e);
    }
	//22222
    mapperListener.start();
}

查看Http11Protocol類的start()方法:

//代碼段15
@Override
public void start() throws Exception {
    if (getLog().isInfoEnabled())
        getLog().info(sm.getString("abstractProtocolHandler.start",
                getName()));
    try {
		//調用  endpoint的start()方法
        endpoint.start();
    } catch (Exception ex) {
        getLog().error(sm.getString("abstractProtocolHandler.startError",
                getName()), ex);
        throw ex;
    }
}

查看JIOEndpoint類的start()方法:

//代碼段16
public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
        bind();
        bindState = BindState.BOUND_ON_START;
    }
    startInternal();
}

因為在init()方法中,bindState已經初始化,所以直接調用startInternal()方法,查看JIOEndPoint()類的startInternal()方法:

//代碼段17
 @Override
public void startInternal() throws Exception {

    if (!running) {
        running = true;
        paused = false;

        // Create worker collection
		// 創建內部線程池 executor
        if (getExecutor() == null) {
			//1111
            createExecutor();
        }
		// 初始化 connectionLimitLatch 組件(一個同步組件,給停止的時候釋放鏈接用的)
        initializeConnectionLatch();
		//2222
        startAcceptorThreads();

        // Start async timeout thread
        Thread timeoutThread = new Thread(new AsyncTimeout(),
                getName() + "-AsyncTimeout");
        timeoutThread.setPriority(threadPriority);
        timeoutThread.setDaemon(true);
        timeoutThread.start();
    }
}

查看createExecutor()方法:

//代碼段18
public void createExecutor() {
    internalExecutor = true;
    TaskQueue taskqueue = new TaskQueue();
	//自定義的ThreadFactory 主要目的是為了能自定義線程池名稱
    TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
	//新建一個線程池,最小線程數量是 getMinSpareThreads() ,最大是 getMaxThreads(),緩存時間60秒,使用 LinkedBlockingQueue 雙端無界隊列
	//如果在server.xml中沒有配置的話的,默認情況 getMinSpareThreads()返回10,getMaxThreads()返回200,也就是 tomcat內部線程池默認最小線程數量10,最大200
    executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
    taskqueue.setParent( (ThreadPoolExecutor) executor);
}

public class TaskQueue extends LinkedBlockingQueue<Runnable>

繼續查看 startAcceptorThreads()方法:

//代碼段19
protected final void startAcceptorThreads() {
	//在代碼段13中 acceptorThreadCount 變量被初始化為1
    int count = getAcceptorThreadCount();
	//新建Acceptor 數組
    acceptors = new Acceptor[count];
	//for 循環賦值
    for (int i = 0; i < count; i++) {
		//賦值11111
        acceptors[i] = createAcceptor();
        String threadName = getName() + "-Acceptor-" + i;
        acceptors[i].setThreadName(threadName);
		//傳遞任務 啟動
        Thread t = new Thread(acceptors[i], threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

public int getAcceptorThreadCount() { return acceptorThreadCount; }

可以看到 代碼段19 主要做的就是新建了一個Acceptor數組並且賦值啟動,而從代碼中可以知道 createAcceptor()方法返回的是一個Runnable的子類,我們查看createAcceptor()方法:

//代碼段20 在JIOEndpoint中
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
    return new Acceptor();
}

 /**
 * The background thread that listens for incoming TCP/IP connections and
 * hands them off to an appropriate processor.

	JIOEndpoint的內部類
 */
protected class Acceptor extends AbstractEndpoint.Acceptor {

    @Override
    public void run() {

        int errorDelay = 0;

        // Loop until we receive a shutdown command
        while (running) {
			
			//各種條件判斷略			

            state = AcceptorState.RUNNING;

            try {
                //if we have reached max connections, wait
                countUpOrAwaitConnection();

                Socket socket = null;
                try {
                    // Accept the next incoming connection from the server
                    // socket
					//1111111111 
                    socket = serverSocketFactory.acceptSocket(serverSocket);
                } catch (IOException ioe) {
                   //異常處理 略
                }
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                if (running && !paused && setSocketOptions(socket)) {
                    // Hand this socket off to an appropriate processor
					//222222222
                    if (!processSocket(socket)) {
                        countDownConnection();
                        // Close socket right away
                        closeSocket(socket);
                    }
                } else {
                    countDownConnection();
                    // Close socket right away
                    closeSocket(socket);
                }
            } catch (IOException x) {
               //異常處理 略
            } catch (NullPointerException npe) {
               //異常處理 略
            } catch (Throwable t) {
                //異常處理 略
            }
        }
        state = AcceptorState.ENDED;
    }
}

原本的源碼比較長,稍微刪減了一些並且加上了注釋。

在標注1的代碼中,serverSocketFactoryserverSocket變量都在代碼段13中進行了初始化,所以我們查看下acceptSocket()方法。

//代碼段21
@Override
public Socket acceptSocket(ServerSocket socket) throws IOException {
    return socket.accept();
}

很簡單就是調用了socketaccept()方法,但是眾所周知accept()是阻塞的方法,所以代碼段19中新建的線程就會阻塞的這里,那這個阻塞是干什么的呢? 其實這個阻塞就是在接收外部的請求,緊接着就是標注2的地方開始處理請求了,具體的處理請求流程我們在下一篇文章中繼續。

到此就是代碼段14的Connectorstart()方法中protocolHandler.start()方法的源碼我們都看完了,我們繼續查看start()方法中的mapperListener.start()的源碼。

//代碼段22
  /**
  * Mapper.
  */
 protected Mapper mapper = new Mapper();
 /**
  * Mapper listener.
  */
 protected MapperListener mapperListener = new MapperListener(mapper, this);


@Override
public void startInternal() throws LifecycleException {
	//設置生命周期狀態
    setState(LifecycleState.STARTING);

    // Find any components that have already been initialized since the
    // MBean listener won't be notified as those components will have
    // already regis tered their MBeans
	// 設置 defaultHostName 變量
    findDefaultHost();
	
	//將engine 以及engine以下所有的自容器全部添加監聽器  mapperListener
    Engine engine = (Engine) connector.getService().getContainer();
    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
			//11111111
            registerHost(host);
        }
    }
}

/**
* 獲取默認的 host
*/
private void findDefaultHost() {
	//先獲取Engine
    Engine engine = (Engine) connector.getService().getContainer();
	//獲取Engine對象中配置的 defaultHost 變量 ( server.xml 中配置的)
    String defaultHost = engine.getDefaultHost();

    boolean found = false;
	
    if (defaultHost != null && defaultHost.length() >0) {
		//獲取engine的所有 子容器(Host)
        Container[] containers = engine.findChildren();

        for (Container container : containers) {
            Host host = (Host) container;
			//遍歷所有Host,如果Engine中默認的host名稱和 當前host名稱相同,設置found為true
            if (defaultHost.equalsIgnoreCase(host.getName())) {
                found = true;
                break;
            }

            String[] aliases = host.findAliases();
			//同上 只是使用host的別名查找
            for (String alias : aliases) {
                if (defaultHost.equalsIgnoreCase(alias)) {
                    found = true;
                    break;
                }
            }
        }
    }

    if(found) {
		//如果 存在就 設置 defaultHostName 變量
        mapper.setDefaultHostName(defaultHost);
    } else {
        log.warn(sm.getString("mapperListener.unknownDefaultHost",
                defaultHost, connector));
    }
}

/**
 * Add this mapper to the container and all child containers
 *
 * @param container
 */
private void addListeners(Container container) {
	//把mapperListener添加到該容器的監聽器中
    container.addContainerListener(this);
    container.addLifecycleListener(this);
	//遍歷子容器 
    for (Container child : container.findChildren()) {
		//遞歸調用該方法,也就是把mapperListener設置到所有的容器中
        addListeners(child);
    }
}

可以看到mapperListenerstart()方法中簡單的部分都直接添加了注釋,還有標注1的地方沒有說明。標注1的地方registerHost()就不深入查看了,主要原因是內部代碼太過繁瑣,但是邏輯很清楚,所以這里就直接講解原理,不查看源碼分析了。registerHost()主要作用是把HostContextWrapper容器全部注冊到mapperListener中,以供后續方法使用!

我們查看下MapperListener類的構造:

//代碼段23
 /**
 * Associated mapper.
 */
private Mapper mapper = null;

/**
 * Associated connector
 */
private Connector connector = null;

可以看出mapperListenerConnector互相持有對方的引用。繼續看Mapper類:

/**
 * Array containing the virtual hosts definitions.
 */
Host[] hosts = new Host[0];

Mapper內部持有Host數組,而這個Host並非我們常說的 那個容器類Host:

 protected static final class Host extends MapElement {

 	public volatile ContextList contextList;

}

這個HostMapper的一個內部類,持有了一個ContextList對象,繼續查看:

 protected static final class ContextList {

    public final Context[] contexts;
}

ContexetList依舊是Mapper的一個內部類,持有了一個Context數組,繼續查看Context類的定義:

protected static final class Context extends MapElement {
    public volatile ContextVersion[] versions;

    public Context(String name, ContextVersion firstVersion) {
        super(name, null);
        versions = new ContextVersion[] { firstVersion };
    }
}

ContextMapper的一個內部類,持有了ContextVersion的數組,查看ContextVersion

protected static final class ContextVersion extends MapElement {
    public String path = null;
    public int slashCount;
    public String[] welcomeResources = new String[0];
    public javax.naming.Context resources = null;
    public Wrapper defaultWrapper = null;
    public Wrapper[] exactWrappers = new Wrapper[0];
    public Wrapper[] wildcardWrappers = new Wrapper[0];
    public Wrapper[] extensionWrappers = new Wrapper[0];
    public int nesting = 0;
    public boolean mapperContextRootRedirectEnabled = false;
    public boolean mapperDirectoryRedirectEnabled = false;
    private volatile boolean paused;
}

可以看到,ContextVersion中持有了 3個Wrapper的數組。具體說到這里可以看出 MapperListener內部包含了所有的Host以及Host的所有自容器,一直到Wrapper,也許看起來不是很直觀,我們debug下看下最終MapperListener的結構:

到此為止,整個Connector從構造函數初始化,到啟動的時候調用init()方法一直到初始化完畢准備接收外部請求的准備工作的源碼已經查看完畢,tomcat處理請求的源碼我們下篇文章繼續查看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM