Tomcat学习笔记(四)-Tomcat运行源码分析

Scroll Down

Tomcat学习笔记(四)-Tomcat运行源码分析

搭建Tomcat源码环境

首先先去官方网站下载Tomcat源码压缩包

image-20200609122234512

这里我已经下载好了,可以直接点击下载,Tomcat8.5源码下载,下载完成之后,解压到本地

Tomcat源码导入Idea

  • 解压 tar.gz 压缩包,得到⽬录 apache-tomcat-8.5.50-src
  • 进⼊ apache-tomcat-8.5.50-src ⽬录,创建⼀个pom.xml⽂件,⽂件内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>apache-tomcat-8.5.50-src</artifactId>
    <name>Tomcat8.5</name>
    <version>8.5</version>
    <build>
        <!--指定源⽬录-->
        <finalName>Tomcat8.5</finalName>
        <sourceDirectory>java</sourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <plugins>
            <!--引⼊编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <!--tomcat 依赖的基础包-->
    <dependencies>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.soap</groupId>
            <artifactId>javax.xml.soap-api</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>
</project>
  • 在 apache-tomcat-8.5.50-src ⽬录中创建 source ⽂件夹

  • 将 conf、webapps ⽬录移动到刚刚创建的 source ⽂件夹中

  • 启动idea,将源码文件夹导入Idea中,等待maven包构建完成

  • 给 tomcat 的源码程序启动类 Bootstrap 配置 VM 参数,因为 tomcat 源码运⾏也需要加载配置⽂件等

-Dcatalina.home=/解压位置/apache-tomcat-8.5.50-
src/source
-Dcatalina.base=/解压位置/apache-tomcat-8.5.50-
src/source
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=/解压位置/apache-
tomcat-8.5.50-src/source/conf/logging.properties

解决Tomcat启动时出现的异常

进入Bootstrap类,启动main函数,会发现有些编译错误,把编译错误给处理掉

image-20200609122849912

使用idea自动提示将问题修复后,重新启动main函数,并访问http://localhost:8080/,发现会有如下报错,

image-20200609123013724

原因是Jsp引擎Jasper没有被初始化,从⽽⽆法编译JSP,我们需要在tomcat的源码ContextConfig类中的configureStart⽅法中增加⼀⾏代码将 Jsp 引擎初始化,context.addServletContainerInitializer(new JasperInitializer(),null);

效果如下
image-20200609123223898

重新启动main函数:发现已经能够正常访问了,这样tomcat的源码环境已经搭建好了

image-20200609123314987

Tomcat启动核心流程过程分析

Lifecycle接口介绍

Tomcat中的各容器组件都会涉及创建、销毁等,因此设计了⽣命周期接⼝Lifecycle进⾏统⼀规范,各容器组件实现该接⼝。

Lifecycle主要方法

image-20200609123444714

Lifecycle主要实现类

image-20200609123743041

Tomcat核心代码流程分析

这里我们源码主要关注两个过程:Tomcat启动流程和Tomcat请求处理流程

Tomcat启动流程

Tomcat启动流程时序图

image-20200609174429020

首先是通过startup.bat运行,最终转到Bootstrap类的main方法执行,然后调用Bootstrap的init方法,容器开始逐步的进行init初始化,初始化完成后进行逐级的启动

  • Bootstrap类中的main方法
public static void main(String args[]) {
        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            //进入start分支,先执行load,load执行完毕之后执行start    
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
    }
  • Catalina的init方法,这里省略了一些不影响主流程的代码,主要作用是解析server.xml配置文件初始化server并调用server的init方法
public void load() {
        // Create and execute our Digester
        //XML解析器,用于解析server.xml
        Digester digester = createStartDigester();
                inputSource.setByteStream(inputStream);
                digester.push(this);
                //解析配置文件,并初始化server
                digester.parse(inputSource);
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
            //启动server
            getServer().init();
    }
  • Server的init方法,会进入org.apache.catalina.util.LifecycleBase#init方法,这里使用模板模式,initInternal()方法是抽象的,具体的initInternal()方法由各个子类实现
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}
    /**
     * Sub-classes implement this method to perform any instance initialisation
     * required.
     *
     * @throws LifecycleException If the initialisation fails
     */
    protected abstract void initInternal() throws LifecycleException;

进入到具体的org.apache.catalina.core.StandardServer#initInternal,

    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();
        ...省略
        // Initialize our defined Services,启动具体的service
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }
  • Service类的init方法,同样进入了org.apache.catalina.util.LifecycleBase#init方法,这里还是一样,需要到具体的子类实现的 initInternal();方法中,最终进入的是org.apache.catalina.core.StandardService#initInternal方法,主要做的有engine的init,executor的init,connector的init
 @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();
		//启动engine
        if (engine != null) {
            engine.init();
        }

        // Initialize any Executors
        // 
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize mapper listener
        mapperListener.init();

        // Initialize our defined Connectors
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

  • 下面进入connector的init,主要做了初始化对应的适配器,并且将适配器设置到protocolHandler中,最后调用protocolHandler#init方法
@Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Initialize adapter
        //初始化对应使用的适配器
        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()));
        }
        if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
                protocolHandler instanceof AbstractHttp11JsseProtocol) {
            AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                    (AbstractHttp11JsseProtocol<?>) protocolHandler;
            if (jsseProtocolHandler.isSSLEnabled() &&
                    jsseProtocolHandler.getSslImplementationName() == null) {
                // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
                jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
            }
        }

        try {
            //初始化protocolHandler
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }
  • protocolHandler.init()方法最终进入org.apache.coyote.AbstractProtocol#init方法中,主要做的是 endpoint.init()的初始化工作以及一些初始化属性设置
    public void init() throws Exception {
        if (getLog().isInfoEnabled()) {
            getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        }

        if (oname == null) {
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) {
                Registry.getRegistry(null, null).registerComponent(this, oname, null);
            }
        }

        if (this.domain != null) {
            rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent(
                    getHandler().getGlobal(), rgOname, null);
        }

        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        endpoint.setDomain(domain);

        endpoint.init();
    }
  • endpoint.init()默认会进入到org.apache.tomcat.util.net.AbstractEndpoint#init的init方法中,这里也是使用的模板模式,具体的bind()方法由子类实现
    public void init() throws Exception {
        if (bindOnInit) {
            bind();
            bindState = BindState.BOUND_ON_INIT;
        }
    }
public abstract void bind() throws Exception;

这里看到endpoint有默认有三个实现类,tomcat8以后默认使用的NioEndpoint

image-20200609182305631

这里bind方法,主要是使用NIO绑定了对应的端口

image-20200609182425086

绑定完成之后,下面进入start阶段

  • 这里Bootstrap使用反射调用了Catalina中的start()方法
    image-20200609182714802

  • 进入Catalina中的start方法,这里类似于上面的init()方法,调用了server的start()方法

 public void start() {

        if (getServer() == null) {
            load();
        }
		...省略代码
        // Start the new server
        try {
            getServer().start();
        } 

    }
  • 同样进入了org.apache.catalina.util.LifecycleBase#start的方法
 public final synchronized void start() throws LifecycleException {
     ...省略代码
        try {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            //启动容器,具体的子类实现
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a 'controlled' failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {
                // Shouldn't be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            handleSubClassException(t, "lifecycleBase.startFail", toString());
        }
    }

  • 进入到org.apache.catalina.core.StandardServer#startInternal方法,启动了对应的service
protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
	
        globalNamingResources.start();

        // Start our defined Services
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                //启动service
                services[i].start();
            }
        }
    }
  • 进入到org.apache.catalina.core.StandardService#startInternal,主要做的有engine的start,executor的start,connector的start
 protected void startInternal() throws LifecycleException {

        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }

        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }

        mapperListener.start();

        // Start our defined Connectors second
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }
  • 进入org.apache.catalina.connector.Connector#startInternal方法,主要执行的有protocolHandler的start方法,并且将容器的状态设置为STARTING
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 {
            protocolHandler.start();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }
  • 然后进入org.apache.coyote.AbstractProtocol#start方法,和init一样,主要做的有endpoint.start();

    public void start() throws Exception {
            if (getLog().isInfoEnabled()) {
                getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
            }
            endpoint.start();
        }
    
  • 这里进入org.apache.tomcat.util.net.AbstractEndpoint#start方法

image-20200609183527802

进入了org.apache.tomcat.util.net.NioEndpoint#startInternal方法,主要功能就是创建Acceptor类,并用于监听Socket,处理前端发送的Socket请求

    public void startInternal() throws Exception {
		...省略代码
            // Create worker collection
            if ( getExecutor() == null ) {
                createExecutor();
            }
        //启动NIO的线程监听
            startAcceptorThreads();
        }
    }
protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        acceptors = new Acceptor[count];

        for (int i = 0; i < count; i++) {
            //创建Acceptor,Acceptor是一个线程类
            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();
        }
    }
protected AbstractEndpoint.Acceptor createAcceptor() {
        return new Acceptor();
    }

Acceptor类run方法实现

protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {

                // Loop if endpoint is paused
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

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

                    SocketChannel socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        //进行serverSock的监听
                        socket = serverSock.accept();
                    } catch (IOException ioe) {
                        // We didn't get a socket
                        countDownConnection();
                        if (running) {
                            // Introduce delay if necessary
                            errorDelay = handleExceptionWithDelay(errorDelay);
                            // re-throw
                            throw ioe;
                        } else {
                            break;
                        }
                    }
                    // Successful accept, reset the error delay
                    errorDelay = 0;

                    // Configure the socket
                    if (running && !paused) {
                        // setSocketOptions() will hand the socket off to
                        // an appropriate processor if successful
                        if (!setSocketOptions(socket)) {
                            closeSocket(socket);
                        }
                    } else {
                        closeSocket(socket);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            //状态设置为结束
            state = AcceptorState.ENDED;
        }

Tomcat请求处理过程分析