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处理请求的源码我们下篇文章继续查看。

原文地址:https://www.cnblogs.com/coldridgeValley/p/6234629.html