tomcat源码--1--启动流程

tomcat源码–1–启动流程

1 启动入口

tomcat和普通的java程序一样,都是通过main()方法启动。tomcatmain()方法定义在其源码的org.apache.catalina.startup.Bootstrap类中。我们先来看这个类中静态代码块。

1.1 Bootstrap类的静态代码块

private static final File catalinaHomeFile;

static {
    // Will always be non-null
    //首先获取到系统属性user.dir,实际上就是项目路径
    String userDir = System.getProperty("user.dir");

    // Home first
    //Constants.CATALINA_HOME_PROP = "catalina.home"
    //就是catalina文件夹的位置,默认是null,我们可以在启动参数中指定
    String home = System.getProperty(Constants.CATALINA_HOME_PROP);
    File homeFile = null;

    //用户在启动参数中指定了catalina.home参数的值,就会进入此分支
    if (home != null) {
        File f = new File(home);
        try {
            //尝试以相对路径的方式获取File对象
            homeFile = f.getCanonicalFile();
        } catch (IOException ioe) {
            //相对路径方式产生异常,则使用绝对路径的方式获取File对象
            homeFile = f.getAbsoluteFile();
        }
    }

    //用户未指定启动参数catalina.home的值,就会进行两次回退操作
    //第一次回退
    if (homeFile == null) {
        // First fall-back. See if current directory is a bin directory
        // in a normal Tomcat install
        //加载项目根路径下的bootstrap.jar包,实际上该jar包在bin目录下
        File bootstrapJar = new File(userDir, "bootstrap.jar");

        //如果tomcat是以bin目录下的startup.bat文件启动的,则会进入下面的if分支
        if (bootstrapJar.exists()) {
            File f = new File(userDir, "..");
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }
    }

    //第二次回退,如果是源码方式启动,则进入下面分支
    if (homeFile == null) {
        // Second fall-back. Use current directory
        //直接使用当前项目根路径
        File f = new File(userDir);
        try {
            homeFile = f.getCanonicalFile();
        } catch (IOException ioe) {
            homeFile = f.getAbsoluteFile();
        }
    }

    //经过上面3次判定,最终得到catalina的路径
    catalinaHomeFile = homeFile;
    //CATALINA_HOME_PROP = "catalina.home"
    //将catalina的资源路径保存到系统属性中
    System.setProperty(
        Constants.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

    // Then base
    //下面是获取并校验catalina的基础路径,确保路径正确
    //CATALINA_BASE_PROP = "catalina.base"
    String base = System.getProperty(Constants.CATALINA_BASE_PROP);
    //指定了catalina.base启动参数,就会使用启动参数的
    if (base == null) {
        catalinaBaseFile = catalinaHomeFile;
    // 否则使用catalina.home的路径    
    } else {
        File baseFile = new File(base);
        try {
            baseFile = baseFile.getCanonicalFile();
        } catch (IOException ioe) {
            baseFile = baseFile.getAbsoluteFile();
        }
        catalinaBaseFile = baseFile;
    }
    //将catalina.base属性保存到系统属性中
    System.setProperty(
        Constants.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}

可以发现,这个静态代码块的作用主要就是获取并校验catalina的路径。

1.2 main()方法

静态代码块执行完成了之后,main()方法就开始执行了,我们接下来看看这个方法做了什么事情。

/**
 * 用来加锁的对象
 */
private static final Object daemonLock = new Object();

//用来保存当前主类对象的引用
private static volatile Bootstrap daemon = null;

/**
 * Main method and entry point when starting Tomcat via the provided
 * scripts.
 *
 * @param args Command line arguments to be processed
 */
public static void main(String args[]) {

    synchronized (daemonLock) {
        //首次daemon字段肯定是null,必然会进入此分支
        if (daemon == null) {
            // Don't set daemon until init() has completed
            //创建启动类的对象,后面会调用这个对象中的三个生命周期方法,来实现tomcat的生命周期过程
            Bootstrap bootstrap = new Bootstrap();
            try {
                //初始化启动器,见2
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            //保存主类对象引用,方便以后使用
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to
            // prevent a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    }

    try {
        //初始命令start,表示启动tomcat
        String command = "start";
        //如果注册参数不为null,则将主方法最后一个参数值作为本次操作的命令
        if (args.length > 0) {
            command = args[args.length - 1];
        }

        //startd
        if (command.equals("startd")) {
            args[args.length - 1] = "start";
            daemon.load(args);
            daemon.start();
        } 
        //stopd
        else if (command.equals("stopd")) {
            args[args.length - 1] = "stop";
            daemon.stop();
        }
        //start,tomcat启动
        else if (command.equals("start")) {
            daemon.setAwait(true);
            daemon.load(args);
            daemon.start();
            if (null == daemon.getServer()) {
                System.exit(1);
            }
        } 
        //stop
        else if (command.equals("stop")) {
            daemon.stopServer(args);
        } 
        //configtest
        else if (command.equals("configtest")) {
            daemon.load(args);
            if (null == daemon.getServer()) {
                System.exit(1);
            }
            System.exit(0);
        } 
        //非上述5种命令,则输出警告日志
        else {
            log.warn("Bootstrap: command \"" + command + "\" does not exist.");
        }
    } catch (Throwable t) {
        // Unwrap the Exception for clearer error reporting
        if (t instanceof InvocationTargetException &&
            t.getCause() != null) {
            t = t.getCause();
        }
        handleThrowable(t);
        t.printStackTrace();
        System.exit(1);
    }
}

很容易发现,启动tomcat,无非就是下面三步

  1. 初始化 bootstrap.init();
  2. 解析server.xml文件 daemon.load(args);
  3. 正式启动 daemon.start();

daemon其实就是bootstrap

接下来,我们就按照这个顺序来看看tomcat具体的启动过程

2 init()方法,初始化类加载器和实例化Catalina

/**
 * catalina对象,方便以后使用
 */
private Object catalinaDaemon = null;


/**
 * Initialize daemon.
 * @throws Exception Fatal initialization error
 */
public void init() throws Exception {

    //初始化类加载器,见2.1
    initClassLoaders();

    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    //使用Catalina类加载器加载Catalina服务类
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    //反射实例化Catalina
    Object startupInstance = startupClass.getConstructor().newInstance();

    // Set the shared extensions class loader
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    //下面这一部分代码实际上就是反射调用Catalina对象的setParentClassLoader()方法,将
    //sharedLoader共享扩展类加载器作为Catalina对象的父类加载器
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);

    //将刚刚反射创建的Catalina对象赋值给Catalina字段
    catalinaDaemon = startupInstance;
}

总结这个方法做的事情:

  1. 创建3个类加载器
  2. 实例化org.apache.catalina.startup.Catalina
  3. 反射调用上面实例化对象的setParentClassLoader()方法,将sharedLoader共享扩展类加载器作为Catalina对象的父类加载器

2.1 初始化类加载器

ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;

private void initClassLoaders() {
    try {
        //公用的类加载器
        commonLoader = createClassLoader("common", null);
        if (commonLoader == null) {
            // no config file, default to this loader - we might be in a 'single' env.
            //将加载当前Bootstrap类的加载器作为公共类加载器
            commonLoader = this.getClass().getClassLoader();
        }
        //创建一个加载catalina服务的类加载器
        catalinaLoader = createClassLoader("server", commonLoader);
        //创建一个共享类加载器
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

该方法初始化3个类加载器

  1. commonLoader
  2. catalinaLoader:用来加载catalina服务的类加载器
  3. sharedLoader

3 load()方法,解析server.xml文件,初始化tomcat

/**
 * Load daemon.
 */
private void load(String[] arguments) throws Exception {

    // Call the load() method
    String methodName = "load";
    Object param[];
    Class<?> paramTypes[];
    if (arguments==null || arguments.length==0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    if (log.isDebugEnabled()) {
        log.debug("Calling startup class " + method);
    }
    method.invoke(catalinaDaemon, param);
}

这个方法流程没什么看的, 主要是通过反射调用Catalina类的load()方法,所以下面的篇幅我们就来看看Catalina类的load()方法做了什么?

/**
 * 防止load()方法重复调用
 */
protected boolean loaded = false;


/**
 * Start a new server instance.
 */
public void load() {

    //load()方法,只允许被调用一次
    if (loaded) {
        return;
    }
    loaded = true;

    long t1 = System.nanoTime();

    //过时方法,从tomcat10开始被删除了
    initDirs();

    // Before digester - it may be needed
    //初始化命名策略
    initNaming();

    // Parse main server.xml
    //解析conf目录下的server.xml文件,并根据配置创建tomcat各种组件对象,具体过程忽略
    parseServerXml(true);
    //获取创建的Server组件对象,默认为StandardServer
    Server s = getServer();
    if (s == null) {
        return;
    }

    //保存当前Catalina的配置到Server组件对象中
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection
    //用自定义的打印流替换System.out和System.err。日志相关配置,不用理会
    initStreams();

    // Start the new server
    try {
        //初始化server组件
        getServer().init();
    } catch (LifecycleException e) {
        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new java.lang.Error(e);
        } else {
            log.error(sm.getString("catalina.initError"), e);
        }
    }

    if(log.isInfoEnabled()) {
        log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
    }
}

可以发现,该方法会先解析server.xml文件,根据文件配置生成对应的组件对象,并形成层级关系

  • Server
  • Service
  • Engine
  • Connector
  • Host
  • Context

此处会调用生成的Server组件init()方法,开始tomcat的初始化过程

我们接下来看一下这几个组件的类图

在这里插入图片描述

很容易发现,这些组件都继承自LifecycleMBeanBase类,那么它们为什么不直接实现Lifecycle接口呢?其实啊,这里用到可一种设计模式–模板方法,它将每一个生命周期方法的公有逻辑提取到LifecycleBase类中,而各自特有的逻辑则由自己本类实现,减少了代码冗余。

下面就是父类LifecycleBaseinit()方法

@Override
public final synchronized void init() throws LifecycleException {
    //第一次调用组件的init()方法,state的状态是NEW,不会进入该分支
    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());
    }
}

后面所有的组件的初始化过程均不贴上面方法代码。

废话不多说,下面开始正文

3.1 Server初始化

/**
 * 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 utility executor
    //创建ScheduledThreadPoolExecutor线程池
    reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
    //将这个定时任务线程池注册到JMX中
    register(utilityExecutor, "type=UtilityExecutor");

    // Register global String cache
    // Note although the cache is global, if there are multiple Servers
    // present in the JVM (may happen when embedding) then the same cache
    // will be registered under multiple names
    onameStringCache = register(new StringCache(), "type=StringCache");

    // Register the MBeanFactory
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");

    // Register the naming resources
    globalNamingResources.init();

    // Populate the extension validator with JARs from common and shared
    // class loaders
    if (getCatalina() != null) {
        ClassLoader cl = getCatalina().getParentClassLoader();
        // Walk the class loader hierarchy. Stop at the system class loader.
        // This will add the shared (if present) and common class loaders
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        try {
                            File f = new File (url.toURI());
                            if (f.isFile() &&
                                f.getName().endsWith(".jar")) {
                                ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException | IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    // Initialize our defined Services
    //上面的代码完全可以忽略掉,等我们弄懂了tomcat之后,再回头来看这些细节。
    //重点在这里,这里会遍历解析server.xml文件service节点创建的对象,调用它的初始化方法
    for (Service service : services) {
        service.init();
    }
}
  • Service的类型为StandardService,它是tomcat定义的唯一实现。
  • Catalinaload()方法会调用它的init()方法, 然后init()方法又会调用每一个Service对象的init()方法。

3.2 初始化Service

那么接下来,我们就看看StandardServiceinit()方法做了什么?

首先,你需要知道的是tomcat中每一个组件都实现了LifecycleBase类,都定义了生命周期方法,StandardService也不例外,所以它的init()方法和Server组件是一样的,我们只需要看initInternal()方法。

protected final MapperListener mapperListener = new MapperListener(this);

//下面这3个字段会根据server.xml文件的解析结果给定初始值
private Engine engine = null;
protected final ArrayList<Executor> executors = new ArrayList<>();
protected Connector connectors[] = new Connector[0];



/**
 * 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();

    // 初始化Engine,见3.2
    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
    // 初始化Connector,见3.3
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();
        }
    }
}

Service组件又初始化了4个组件

  1. Engine:唯一实现StandardEngine
  2. Executors,默认未在server.xml中配置<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
  3. listener
  4. Connector

3.3 初始化Engine

@Override
protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    // 身份认证相关,见3.31
    getRealm();
    // 调用父类的方法初始化了一个线程池,这个线程池非常重要,
    // 需要重点关注,后面很多地方都会用到,见3.32
    super.initInternal();
}

初始化Engine的过程中会做两件事情

  1. 初始化一个Realm,用来做身份认证
  2. 创建一个线程池,供后续使用

3.3.1 身份认证相关

/**
 * Obtain the configured Realm and provide a default Realm implementation
 * when no explicit configuration is set.
 *
 * @return configured realm, or a {@link NullRealm} by default
 */
@Override
public Realm getRealm() {
    //获取当前引擎的Realm,默认为LockOutRealm
    Realm configured = super.getRealm();
    // If no set realm has been called - default to NullRealm
    // This can be overridden at engine, context and host level
    //未指定,则使用NullRealm
    if (configured == null) {
        configured = new NullRealm();
        this.setRealm(configured);
    }
    return configured;
}

Realm其实是用来进行身份认证的,了解过shiro的应该知道,shiro中就是使用Realm来进行认证授权的

3.3.2 创建一个线程池startStopExecutor

@Override
protected void initInternal() throws LifecycleException {
    // 初始化线程池
    reconfigureStartStopExecutor(getStartStopThreads());
    super.initInternal();
}


private void reconfigureStartStopExecutor(int threads) {
    // 使用新的线程池
    if (threads == 1) {
        // Use a fake executor
        if (!(startStopExecutor instanceof InlineExecutorService)) {
            startStopExecutor = new InlineExecutorService();
        }
    } 
    // 使用全局的线程池
    else {
        // Delegate utility execution to the Service
        Server server = Container.getService(this).getServer();
        server.setUtilityThreads(threads);
        startStopExecutor = server.getUtilityExecutor();
    }
}

我们可以发现,Engine初始化最终就是初始化一个线程池startStopExecutor,供后续使用

3.4 初始化Connector

// 解析server.xml的connertor节点时,根据配置生成一个Connector和ProtocolHandler
// 默认情况下为Http11NioProtocol,即http1.1协议
protected final ProtocolHandler protocolHandler;


@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //如果配置的协议不正确,就不能自动生成一个ProtocolHandler对象,tomcat就会报错
    if (protocolHandler == null) {
        throw new LifecycleException(
            sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }

    // Initialize adapter
    // Coyote用来处理请求,负责将Request适配为HttpServletRequest对象
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    // 将在server中配置的公用线程池应用到协议上
    if (service != null) {
        protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
    }

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

    //下面三个都是校验配置
    if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                                                  getProtocolHandlerClassName()));
    }
    if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                                                  getProtocolHandlerClassName()));
    }
    if (AprStatus.isAprAvailable() && AprStatus.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);
    }
}

Connector初始化做的事情

  1. 校验用户在server.xmlconnector节点的配置是否正确
  2. 带动ProtocolHandler进行初始化,见3.5

3.5 初始化ProtocolHandler

@Override
public void init() throws Exception {
    // Upgrade protocols have to be configured first since the endpoint
    // init (triggered via super.init() below) uses this list to configure
    // the list of ALPN protocols to advertise
    for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
        configureUpgradeProtocol(upgradeProtocol);
    }

    // 
    super.init();

    // Set the Http11Protocol (i.e. this) for any upgrade protocols once
    // this has completed initialisation as the upgrade protocols may expect this
    // to be initialised when the call is made
    for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
        upgradeProtocol.setHttp11Protocol(this);
    }
}

核心逻辑在父类init()方法中

/**
 * Endpoint that provides low-level network I/O - must be matched to the
 * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
 * Endpoint etc.).
 * tomcat 9 默认为NioEndpoint
 */
private final AbstractEndpoint<S,?> endpoint;



/*
 * NOTE: There is no maintenance of state or checking for valid transitions
 * within this class. It is expected that the connector will maintain state
 * and prevent invalid state transitions.
 */

@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        logPortOffset();
    }

    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) {
        ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        this.rgOname = rgOname;
        Registry.getRegistry(null, null).registerComponent(
            getHandler().getGlobal(), rgOname, null);
    }

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

    // 初始化Endpoint,绑定端口和主机
    endpoint.init();
}

带动Endpoint初始化,绑定端口和主机

private boolean bindOnInit = true;
private volatile BindState bindState = BindState.UNBOUND;

public final void init() throws Exception {
    if (bindOnInit) {
        // 绑定端口和主机
        bindWithCleanup();
        // 将端点状态置为BindState.BOUND_ON_INIT,表明已经初始化
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                                                          ":type=SocketProperties,name=\"" + getName() + "\"");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}


private void bindWithCleanup() throws Exception {
    try {
        // 子类NioEndpoint重写该方法,完成绑定
        bind();
    } catch (Throwable t) {
        // Ensure open sockets etc. are cleaned up if something goes
        // wrong during bind
        ExceptionUtils.handleThrowable(t);
        unbind();
        throw t;
    }
}

子类NioEndpoint重写bind()方法, 完成绑定

/**
 * Initialize the endpoint.
 */
@Override
public void bind() throws Exception {
    // 绑定端口和主机
    initServerSocket();

    setStopLatch(new CountDownLatch(1));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open(getName());
}
// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
    if (getUseInheritedChannel()) {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    } else if (getUnixDomainSocketPath() != null) {
        SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());
        serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();
        serverSock.bind(sa, getAcceptCount());
        if (getUnixDomainSocketPathPermissions() != null) {
            FileAttribute<Set<PosixFilePermission>> attrs =
                PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(getUnixDomainSocketPathPermissions()));
            Path path = Paths.get(getUnixDomainSocketPath());
            Files.setAttribute(path, attrs.name(), attrs.value());
        }
    } 
    // 绑定过程走这个分支
    else {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
        serverSock.bind(addr, getAcceptCount());
    }
    // 设置阻塞
    serverSock.configureBlocking(true); //mimic APR behavior
}

了解NIO的应该知道,这上面就服务器端的程序的创建的过程

4 start()方法,启动tomcat服务

/**
 * Start the Catalina daemon.
 * @throws Exception Fatal start error
 */
public void start() throws Exception {
    if (catalinaDaemon == null) {
        init();
    }

    // 反射调用Catalina的start()方法
    Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
    method.invoke(catalinaDaemon, (Object [])null);
}

该方法反射调用Catalinastart()方法,我们重点看Catalinastart()方法

/**
 * Start a new server instance.
 */
public void start() {

    // 如果没有Server,就重写调用load()方法解析server.xml
    if (getServer() == null) {
        load();
    }

    // 还找不到,直接返回,tomcat不能正常启动
    if (getServer() == null) {
        log.fatal(sm.getString("catalina.noServer"));
        return;
    }

    long t1 = System.nanoTime();

    // Start the new server
    try {
        // 启动server,见4.1
        getServer().start();
    } catch (LifecycleException e) {
        log.fatal(sm.getString("catalina.serverStartFail"), e);
        try {
            getServer().destroy();
        } catch (LifecycleException e1) {
            log.debug("destroy() failed for failed Server ", e1);
        }
        return;
    }

    if (log.isInfoEnabled()) {
        log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
    }

    if (generateCode) {
        // Generate loader which will load all generated classes
        generateLoader();
    }

    // Register shutdown hook
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // If JULI is being used, disable JULI's shutdown hook since
        // shutdown hooks run in parallel and log messages may be lost
        // if JULI's hook completes before the CatalinaShutdownHook()
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                false);
        }
    }

    if (await) {
        await();
        stop();
    }
}

start()方法是生命周期方法,所以这里我将它们父类LifecycleBasestart()方法先展示出来,后面不在贴这个方法的源码了

下面就是父类LifecycleBaseinit()方法

/**
 * {@inheritDoc}
 */
@Override
public final synchronized void start() throws LifecycleException {

    // 三种状态之一就表明tomcat已经启动或正在启动,那么就跳过后面的启动流程
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
        LifecycleState.STARTED.equals(state)) {

        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }

        return;
    }

    // 根据状态执行对应的操作
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
               !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
        // 设置状态为STARTING_PREP预启动
        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());
    }
}

该方法是LifecycleBase的一个方法, 用来包装所有组件的启动过程,真正的业务逻辑都在startInternal()方法中

4.1 启动Server

/**
 * Start nested components ({@link Service}s) and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected void startInternal() throws LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    // 设置状态为启动中
    setState(LifecycleState.STARTING);

    // 全局命名服务启动
    globalNamingResources.start();

    // Start our defined Services
    // 启动Service组件
    synchronized (servicesLock) {
        for (Service service : services) {
            service.start();
        }
    }

    if (periodicEventDelay > 0) {
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
            () -> startPeriodicLifecycleEvent(), 0, 60, TimeUnit.SECONDS);
    }
}

带动Service组件启动

4.2 启动Service

/**
 * Start nested components ({@link Executor}s, {@link Connector}s and
 * {@link Container}s) and implement the requirements of
 * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
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,见4.3
            engine.start();
        }
    }

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

    mapperListener.start();

    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                // 启动Connector,见4.4
                connector.start();
            }
        }
    }
}

在这个方法中,我们重点关注EngineConnectorstart()方法,看看它是如何启动这两个组件的?

4.3 启动Engine

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Log our server identification information
    if (log.isInfoEnabled()) {
        log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
    }

    // Standard container startup
    // 父类方法会判断它有没有子容器,有的话,就将这个子容器的处理过程交给线程池
    super.startInternal();
}

下面是它父类的startInternal()方法

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    logger = null;
    getLogger();
    // Cluster以后再说,现在还不知道有什么作用
    Cluster cluster = getClusterInternal();
    if (cluster instanceof Lifecycle) {
        ((Lifecycle) cluster).start();
    }
    Realm realm = getRealmInternal();
    if (realm instanceof Lifecycle) {
        ((Lifecycle) realm).start();
    }

    // Start our child containers, if any
    // 查找拥有的子容器
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (Container child : children) {
        // 包装为StartChild,交给线程池处理
        results.add(startStopExecutor.submit(new StartChild(child)));
    }

    MultiThrowable multiThrowable = null;

    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Throwable e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            if (multiThrowable == null) {
                multiThrowable = new MultiThrowable();
            }
            multiThrowable.add(e);
        }

    }
    if (multiThrowable != null) {
        throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                                     multiThrowable.getThrowable());
    }

    // Start the Valves in our pipeline (including the basic), if any
    // 启动流水线Pipeline,你可以把它理解为过滤器链,Valve理解为过滤器
    if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }

    setState(LifecycleState.STARTING);

    // Start our thread
    if (backgroundProcessorDelay > 0) {
        monitorFuture = Container.getService(ContainerBase.this).getServer()
            .getUtilityExecutor().scheduleWithFixedDelay(
            new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
    }
}

总结一下Engine启动做的事情

  1. Engine的子容器Host的处理过程交给线程池处理。注意,这个Host是用户在server.xml文件中配置Host节点的那个,如果用户配置了多个Host节点,那么就会使用多个线程处理,加快启动速度。
  2. 启动流水线Pipeline,你可以把它理解为过滤器链,Valve理解为过滤器

4.3.1 启动Host(由另外的线程启动)

我们先来看一下这个StartChild,这个类是ContainerBase的一个嵌套类,没什么逻辑,主要实现了Callable接口,用来启动Host

private static class StartChild implements Callable<Void> {

    private Container child;

    public StartChild(Container child) {
        this.child = child;
    }

    @Override
    public Void call() throws LifecycleException {
        // 子容器的Start()方法启动子容器
        child.start();
        return null;
    }
}

下面是这4个容器的关系图

在这里插入图片描述

EngineContextHostWrapper都是一个容器,实现Container接口,都继承了ContainerBase类。并且它们之间的层级关系是Engine->Host->Context->Wrapper

接下来我们就看看启动过程中做了什么?

所有子容器组件都实现了生命周期接口Lifecycle,所以我们直接看它启动的核心方法startInternal()

/**
 * The Java class name of the default error reporter implementation class
 * for deployed web applications.
 * 主机使用的错误报告Valve类的完全限定名,已经写死在StandardHost类中
 */
private String errorReportValveClass =
    "org.apache.catalina.valves.ErrorReportValve";



/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Set error report valve
    // 获取当前主机使用的错误报告Valve类的完全限定名
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) && (!errorValve.equals(""))) {
        try {
            boolean found = false;
            // 得到当前主机下配置的所有Valve类
            // 默认情况下有两个,分别为AccessLogValve,StandardHostValve
            // AccessLogValve是在server.xml文件中配置的
            // StandardHostValue则是Host自带的
            Valve[] valves = getPipeline().getValves();
            // 遍历,如果用户在server.xml中配置了,那就使用用户配置的
            for (Valve valve : valves) {
                // 比较两个类的完全限定名
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }
            // 否则使用系统默认的错误报告Valve
            if(!found) {
                // 反射实例化系统默认的错误报告Valve类
                Valve valve = ErrorReportValve.class.getName().equals(errorValve) ?
                    new ErrorReportValve() :
                (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString(
                "standardHost.invalidErrorReportValveClass",
                errorValve), t);
        }
    }
    // 这个方法流程和StandardEngine是完全一模一样的,都是父类ContainerBase的方法,主要做三个事情
    // 1.它也会去找它的子容器Context,并把它的处理过程交给线程池完成
    // 2.还会启动Host中的Pepeline,三个,
    // 分别为ErrorReportValve,AccessLogValve,StandardHostValue
    // 3.发布事件LifecycleState.STARTING,HostConfig监听到该事件后,
    // 开始扫描解析webapps目录下的war包和Context,见(1-3)
    super.startInternal();
}

下面是父类ContainerBasestartInternal()方法

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    logger = null;
    getLogger();
    // Cluster以后再说,现在还不知道有什么作用
    Cluster cluster = getClusterInternal();
    if (cluster instanceof Lifecycle) {
        ((Lifecycle) cluster).start();
    }
    Realm realm = getRealmInternal();
    if (realm instanceof Lifecycle) {
        ((Lifecycle) realm).start();
    }

    // Start our child containers, if any
    // 查找拥有的子容器Context
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (Container child : children) {
        // 包装为StartChild,交给线程池处理
        results.add(startStopExecutor.submit(new StartChild(child)));
    }

    MultiThrowable multiThrowable = null;

    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Throwable e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            if (multiThrowable == null) {
                multiThrowable = new MultiThrowable();
            }
            multiThrowable.add(e);
        }

    }
    if (multiThrowable != null) {
        throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                                     multiThrowable.getThrowable());
    }

    // Start the Valves in our pipeline (including the basic), if any
    // 启动流水线Pipeline,你可以把它理解为过滤器链,Valve理解为过滤器
    // 启动三个ErrorReportValve,AccessLogValve,StandardHostValue,见(1-2)
    if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }

    // 发布事件,扫描解析webapps目录,见(1-3)
    setState(LifecycleState.STARTING);

    // Start our thread
    if (backgroundProcessorDelay > 0) {
        monitorFuture = Container.getService(ContainerBase.this).getServer()
            .getUtilityExecutor().scheduleWithFixedDelay(
            new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
    }
}

4.3.2 启动Pipeline

Pipeline也实现了生命周期接口Lifecycle,所以我们直接看它启动的核心方法startInternal()

/**
 * The first valve associated with this Pipeline.
 */
protected Valve first = null;


/**
 * Start {@link Valve}s) in this pipeline and implement the requirements
 * of {@link LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start the Valves in our pipeline (including the basic), if any
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    // 遍历,启动容器拥有的每一个valve
    while (current != null) {
        if (current instanceof Lifecycle)
            ((Lifecycle) current).start();
        current = current.getNext();
    }

    // 发布事件,被观察者监听处理,这里的观察者数量为0
    setState(LifecycleState.STARTING);
}

StandardHost会启动三个Valve,分别为ErrorReportValveAccessLogValveStandardHostValue

4.3.3 发布事件,扫描解析webapps目录

/**
 * Provides a mechanism for sub-classes to update the component state.
 * Calling this method will automatically fire any associated
 * {@link Lifecycle} event. It will also check that any attempted state
 * transition is valid for a sub-class.
 *
 * @param state The new state for this component
 * @throws LifecycleException when attempting to set an invalid state
 */
protected synchronized void setState(LifecycleState state) throws LifecycleException {
    setStateInternal(state, null, true);
}
private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
    throws LifecycleException {

    if (log.isDebugEnabled()) {
        log.debug(sm.getString("lifecycleBase.setState", this, state));
    }

    if (check) {
        // Must have been triggered by one of the abstract methods (assume
        // code in this class is correct)
        // null is never a valid state
        if (state == null) {
            invalidTransition("null");
            // Unreachable code - here to stop eclipse complaining about
            // a possible NPE further down the method
            return;
        }

        // Any method can transition to failed
        // startInternal() permits STARTING_PREP to STARTING
        // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
        // STOPPING
        // 检查组件状态
        if (!(state == LifecycleState.FAILED ||
              (this.state == LifecycleState.STARTING_PREP &&
               state == LifecycleState.STARTING) ||
              (this.state == LifecycleState.STOPPING_PREP &&
               state == LifecycleState.STOPPING) ||
              (this.state == LifecycleState.FAILED &&
               state == LifecycleState.STOPPING))) {
            // No other transition permitted
            invalidTransition(state.name());
        }
    }

    this.state = state;
    // lifecycleEvent="start"
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        // 发布事件
        fireLifecycleEvent(lifecycleEvent, data);
    }
}
/**
 * The list of registered LifecycleListeners for event notifications.
 * 默认情况下,里面只有一个监听器,那就是HostConfig
 */
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();


/**
 * Allow sub classes to fire {@link Lifecycle} events.
 *
 * @param type  Event type
 * @param data  Data associated with event.
 */
protected void fireLifecycleEvent(String type, Object data) {
    // this就是当前StandardHost对象,type为"start"
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        // HostConfig完成对webapps目录的扫描解析
        listener.lifecycleEvent(event);
    }
}

其实这个setState()方法就是用来发布事件的,典型的观察者模式

注意:HostConfig扫描到Context后,会自动的将其添加到Host中,作为它的子容器,并同时调用这些子容器的start()方法, 启动子容器Context

4.4 启动Connector

/**
 * Begin processing requests via this Connector.
 *
 * @exception LifecycleException if a fatal startup error occurs
 */
@Override
protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getProperty("unixDomainSocketPath") == null && getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
            "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }

    // 虽然发布了事件,但是并没有监听器监听
    setState(LifecycleState.STARTING);

    try {
        // 启动ProtocolHandler
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
            sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

Connector只是简单的带动ProtocolHandler启动

4.5 启动ProtocolHandler

tomcat 9使用的是Http11NioProtocol

/**
 * Endpoint that provides low-level network I/O - must be matched to the
 * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
 * Endpoint etc.).
 */
private final AbstractEndpoint<S,?> endpoint;


@Override
public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        logPortOffset();
    }

    // 启动Endipoint
    endpoint.start();
    monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
        () -> {
            if (!isPaused()) {
                startAsyncTimeout();
            }
        }, 0, 60, TimeUnit.SECONDS);
}

带动NioEndpoint启动

private volatile BindState bindState = BindState.UNBOUND;

public final void start() throws Exception {
    // 此时不为UNBOUND,Endpoint初始化的时候已经绑定端口和主机了
    if (bindState == BindState.UNBOUND) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_START;
    }
    // 启动NioEndpoint
    startInternal();
}

启动NioEndpoint

/**
 * Start the NIO endpoint, creating acceptor, poller threads.
 */
@Override
public void startInternal() throws Exception {

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

        if (socketProperties.getProcessorCache() != 0) {
            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                     socketProperties.getProcessorCache());
        }
        if (socketProperties.getEventCache() != 0) {
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                 socketProperties.getEventCache());
        }
        if (socketProperties.getBufferPool() != 0) {
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                  socketProperties.getBufferPool());
        }

        // Create worker collection
        if (getExecutor() == null) {
            createExecutor();
        }

        initializeConnectionLatch();

        // Start poller thread
        // 这个Poller很重要,它实现了Runable接口,Acceptor接收到Socket连接后
        // 就将Socket全权交给这个Poller异步处理
        poller = new Poller();
        Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
        pollerThread.setPriority(threadPriority);
        pollerThread.setDaemon(true);
        pollerThread.start();

        // 启动Acceptor,开始接收Socket连接
        startAcceptorThread();
    }
}

启动Acceptor,开始接收Socket连接

Acceptor由一个单独的线程处理,也就是说有一个线程专门接收Socket连接,但不处理,处理过程交给Poller

protected void startAcceptorThread() {
    acceptor = new Acceptor<>(this);
    String threadName = getName() + "-Acceptor";
    acceptor.setThreadName(threadName);
    Thread t = new Thread(acceptor, threadName);
    t.setPriority(getAcceptorThreadPriority());
    t.setDaemon(getDaemon());
    t.start();
}

接下来看看Acceptor源码,它是如何接收一个Socket连接的?

4.6 Acceptor接收Socket连接

@Override
public void run() {

    int errorDelay = 0;

    try {
        // Loop until we receive a shutdown command
        while (!stopCalled) {

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

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

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

                // Endpoint might have been paused while waiting for latch
                // If that is the case, don't accept new connections
                if (endpoint.isPaused()) {
                    continue;
                }

                U socket = null;
                try {
                    // Accept the next incoming connection from the server
                    // socket
                    // 线程阻塞,接收连接,见4.6.1
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // We didn't get a socket
                    endpoint.countDownConnection();
                    if (endpoint.isRunning()) {
                        // 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 (!stopCalled && !endpoint.isPaused()) {
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    // 把连接交给Poller处理,见4.6.2
                    if (!endpoint.setSocketOptions(socket)) {
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                String msg = sm.getString("endpoint.accept.fail");
                // APR specific.
                // Could push this down but not sure it is worth the trouble.
                if (t instanceof Error) {
                    Error e = (Error) t;
                    if (e.getError() == 233) {
                        // Not an error on HP-UX so log as a warning
                        // so it can be filtered out on that platform
                        // See bug 50273
                        log.warn(msg, t);
                    } else {
                        log.error(msg, t);
                    }
                } else {
                    log.error(msg, t);
                }
            }
        }
    } finally {
        stopLatch.countDown();
    }
    state = AcceptorState.ENDED;
}

4.6.1 接收Socket连接

/**
 * Server socket "pointer".
 */
private volatile ServerSocketChannel serverSock = null;


@Override
protected SocketChannel serverSocketAccept() throws Exception {
    return serverSock.accept();
}

终于看到原生的nio接收连接的代码了,不容易啊

4.6.2 把连接交给Poller处理

/**
 * Process the specified connection.
 * @param socket The socket channel
 * @return <code>true</code> if the socket was correctly configured
 *  and processing may continue, <code>false</code> if the socket needs to be
 *  close immediately
 */
@Override
protected boolean setSocketOptions(SocketChannel socket) {
    NioSocketWrapper socketWrapper = null;
    try {
        // Allocate channel and wrapper
        NioChannel channel = null;
        if (nioChannels != null) {
            channel = nioChannels.pop();
        }
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                socketProperties.getAppReadBufSize(),
                socketProperties.getAppWriteBufSize(),
                socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(bufhandler);
            }
        }
        //先包装为NioSocketWrapper
        NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
        channel.reset(socket, newWrapper);
        connections.put(socket, newWrapper);
        socketWrapper = newWrapper;

        // Set socket properties
        // Disable blocking, polling will be used
        socket.configureBlocking(false);
        if (getUnixDomainSocketPath() == null) {
            socketProperties.setProperties(socket.socket());
        }

        socketWrapper.setReadTimeout(getConnectionTimeout());
        socketWrapper.setWriteTimeout(getConnectionTimeout());
        socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
        // 然后注册到Poller对象中,由Poller处理
        poller.register(socketWrapper);
        return true;
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        try {
            log.error(sm.getString("endpoint.socketOptionsError"), t);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
        if (socketWrapper == null) {
            destroySocket(socket);
        }
    }
    // Tell to close the socket if needed
    return false;
}

Acceptor不负责处理接收到的socket连接,而是把它交给了Poller处理

到此为止,tomcat的启动流程就说完了,但是还有一些细节地方没有探究,后面会补上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值