tomcat原理解析(四):启动处理

一 概述

  好久没有继续更新tomat相关文章了,今天刚好工作上没有什么事情做,就写写相关tomat的理解了。最近也看了别人写的tomcat理解,感觉都比自己写的好,不仅语言描述准确而且内容有深度干货也比较多。我就按照看代码的理解写写了,内容会比较肤浅。前面分析了tomcat在准备启动前做相关资源初始化处理,现在来分析下正在的启动处理代码。

二 tomcat启动处理

a.我这里仍然从Bootstrap类的main函数中daemon.start();入口开始分析,跟进到start()方法的代码如下:
    public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();

        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);

    }
首先判断catalinaDaemon属性是否为空,否则调用init()方法完成初始化,否则通过反射机制调用了catalinaDaemon对象的start方法。catalinaDaemon这个对象就是org.apache.catalina.startup.Catalina类的对象。
b.进入到org.apache.catalina.startup.Catalina类的start()方法查看代码。如下:
public void start() {

        if (getServer() == null) {//判断server对象是否为空,为空就调用load初始化
            load();
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        try {
            getServer().start(); //拿到server对象,调用start方法启动
        } catch (LifecycleException e) {
            log.error("Catalina.start: ", e);
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled())
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");

        try {
            // 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);
                }
            }
        } catch (Throwable t) {
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }

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

    }
上面代码主要涉及两部操作
  • 1.判断tomcat的最外层容器server对象是否已经初始化,如果没有那么调用load进行初始化处理
  • 2.getServer拿到已经初始化成功的server对象调用start()方法启动服务,这里也就是整个容器启动的入口处
c.跟进server对象的start()方法,其调用的是LifecycleBase抽象类的start()方法,查看start方法中代码最终还是调用了startInternal()方法,而且发现startInternal()方法在LifecycleBase类中是抽象的方法。按抽象模版方法模式,调用startInternal()其实调用的是子类的具体实现。我们看看LifecycleBase类中的代码
public synchronized final void start() throws LifecycleException {
        
        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.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        setState(LifecycleState.STARTING_PREP);

        try {
            startInternal(); //调用LifecycleBase类中的抽象方法startInternal方法
        } catch (LifecycleException e) {
            setState(LifecycleState.FAILED);
            throw e;
        }

        if (state.equals(LifecycleState.FAILED) ||
                state.equals(LifecycleState.MUST_STOP)) {
            stop();
        } else {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            if (!state.equals(LifecycleState.STARTING)) {
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            }
            
            setState(LifecycleState.STARTED);
        }
    }
下面就是LifecycleBase类中的抽象方法。
protected abstract void startInternal() throws LifecycleException;
d.前面我们介绍过server的表中实现是StandardServer类,查看该类的startInternal具体实现。代码如下:
 protected void startInternal() throws LifecycleException {

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

        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();//循环调用service接口实现类的start方法来启动容器
            }
        }
    }
StandardServer类中的startInternal做了一件事情,就是通过遍历Service数组循环启动service对象,调用start()方法。前面架构图中介绍过service接口的标准实现是StandardService类,所以start方法按照前面介绍的语意,仍然还是调用StandardService类中的startInternal方法。

e.查看StandardService的startInternal方法启动代码。如下:

    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 (container != null) {
            synchronized (container) {
                container.start(); //StandardEngine启动
            }
        }

        synchronized (executors) {
            for ( int i=0; i<executors.size(); i++ ) {
                executors.get(i).start(); //线程池启动
            }
        }

        // Start our defined Connectors second
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                ((Lifecycle) connectors[i]).start(); //连接器启动
            }
        }
    }

StandardService类中的startInternal方法做了3件事情

  • 1.启动StandardEngine容器
  • 2.循环启动线程池。没看太懂这里的线程池是做什么用的?后面有时间再研究下!
  • 3.循环启动Connector数组。
f.这里我就直接跟踪tomcat启动的主流程了,其它细节的地方就不仔细看啦!立即进入到Connector类的startInternal方法
protected void startInternal() throws LifecycleException {

        setState(LifecycleState.STARTING);

        // Protocol handlers do not follow Lifecycle conventions.
        // protocolHandler.init() needs to wait until the connector.start()
        try {
            protocolHandler.init();   //初始化协议处理,protocolHandler时一个接口,具体实现看具体实现

        } catch (Exception e) {
            throw new LifecycleException
                    (sm.getString
                            ("coyoteConnector.protocolHandlerInitializationFailed", e));
        }

        try {
            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));
        }

        // MapperListener doesn't follow Lifecycle conventions either
        mapperListener.init();
    }
g.初始化协议处理,protocolHandler属性是一个ProtocolHandler类型的,ProtocolHandler接口类的具体实现是用的什么类,我们查看Connector类的带参构造函数。代码如下:
    public Connector(String protocol) {
        setProtocol(protocol);
        // Instantiate protocol handler
        try {
            Class<?> clazz = Class.forName(protocolHandlerClassName);
            this.protocolHandler = (ProtocolHandler) clazz.newInstance();
        } catch (Exception e) {
            log.error
                (sm.getString
                 ("coyoteConnector.protocolHandlerInstantiationFailed", e));
        }
    }
可以看出Connector类在实例化的时候传入了一个字符串protocol参数。在前面章节《tomcat原理解析(三):资源初始化》介绍Connector初始化的时候,通过Connector con = new Connector(attributes.getValue("protocol"));来实例化。这里attributes.getValue("protocol")取的值实际就是在解析server.xml时的
<Connector port="80" protocol="HTTP/1.1"   connectionTimeout="20000"    redirectPort="8443" /> protocol节点的值。通过server.xml配置文件看出在初始化Connector连接器的时候已经默认指定了协议处理器对象。到这里继续跟进setProtocol(protocol);中代码。如下:
    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 {
            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);
            }
        }

    }
以上的方法跟进传入的protocol字符串来区分使用什么类型的协议处理器类。在确定使用什么协议处理后,通过Class.forName来实例化协议处理器对象。在这里tomcat默认使用的org.apache.coyote.http11.Http11Protocol协议处理。
我们看下Http11Protocol类中init做了什么事情,代码如下:
@Override
    public void init() throws Exception {
        ((JIoEndpoint)endpoint).setName(getName());//设置名称
        ((JIoEndpoint)endpoint).setHandler(cHandler);//设置连接对象处理器,在拿到socket对象后会传入处理器中

        // Verify the validity of the configured socket factory
        try {//根据条件初始化socketFacor
            if (isSSLEnabled()) {
                sslImplementation =
                    SSLImplementation.getInstance(sslImplementationName);
                socketFactory = sslImplementation.getServerSocketFactory();
                ((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);
            } else if (socketFactoryName != null) {
                socketFactory = (ServerSocketFactory) Class.forName(socketFactoryName).newInstance();
                ((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);
            }
        } catch (Exception ex) {
            log.error(sm.getString("http11protocol.socketfactory.initerror"),
                      ex);
            throw ex;
        }

        if (socketFactory!=null) {
            Iterator<String> attE = attributes.keySet().iterator();
            while( attE.hasNext() ) {
                String key = attE.next();
                Object v=attributes.get(key);
                socketFactory.setAttribute(key, v);
            }
        }
        
        try {
            endpoint.init();//JioEndpoint对象初始化处理
        } catch (Exception ex) {
            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
            throw ex;
        }
        if (log.isInfoEnabled())
            log.info(sm.getString("http11protocol.init", getName()));

    }
 上面的初始化处理主要做了3件事情
1.设置JIoEndpoint的名称
2.设置Http11ConnectionHandler类连接对象处理
3.调用JIoEndpoint类的init方法进行初始化。
我们看看JIoEndpoint类中的inti方法中的代码,代码如下:
 public void init()
        throws Exception {

        if (initialized)
            return;
        
        // Initialize thread count defaults for acceptor
        if (acceptorThreadCount == 0) {
            acceptorThreadCount = 1;//设置连接器中接收socket请求的线程数量
        }
        if (serverSocketFactory == null) {
            serverSocketFactory = ServerSocketFactory.getDefault();//得到默认创建socket的对象,默认使用的DefaultServerSocketFactory类
        }
        if (isSSLEnabled()) { //设置了一系列的属性,貌似跟socket的参数有关系,具体还不是非常清楚
            serverSocketFactory.setAttribute(SSL_ATTR_ALGORITHM,
                    getAlgorithm());
            serverSocketFactory.setAttribute(SSL_ATTR_CLIENT_AUTH,
                    getClientAuth());
            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_FILE,
                    getKeystoreFile());
            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PASS,
                    getKeystorePass());
            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_TYPE,
                    getKeystoreType());
            serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PROVIDER,
                    getKeystoreProvider());
            serverSocketFactory.setAttribute(SSL_ATTR_SSL_PROTOCOL,
                    getSslProtocol());
            serverSocketFactory.setAttribute(SSL_ATTR_CIPHERS,
                    getCiphers());
            serverSocketFactory.setAttribute(SSL_ATTR_KEY_ALIAS,
                    getKeyAlias());
            serverSocketFactory.setAttribute(SSL_ATTR_KEY_PASS,
                    getKeyPass());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_FILE,
                    getTruststoreFile());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PASS,
                    getTruststorePass());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_TYPE,
                    getTruststoreType());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PROVIDER,
                    getTruststoreProvider());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_ALGORITHM,
                    getTruststoreAlgorithm());
            serverSocketFactory.setAttribute(SSL_ATTR_CRL_FILE,
                    getCrlFile());
            serverSocketFactory.setAttribute(SSL_ATTR_TRUST_MAX_CERT_LENGTH,
                    getTrustMaxCertLength());
            serverSocketFactory.setAttribute(SSL_ATTR_SESSION_CACHE_SIZE,
                    getSessionCacheSize());
            serverSocketFactory.setAttribute(SSL_ATTR_SESSION_TIMEOUT,
                    getSessionTimeout());
            serverSocketFactory.setAttribute(SSL_ATTR_ALLOW_UNSAFE_RENEG,
                    getAllowUnsafeLegacyRenegotiation());
        }

        if (serverSocket == null) { //serverSocket对象为空时即刻创建
            try {
                if (getAddress() == null) {
                    serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog());//根据端口号创建serverSocket对象
                } else {
                    serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog(), getAddress());
                }
            } catch (BindException orig) {
                String msg;
                if (getAddress() == null)
                    msg = orig.getMessage() + " <null>:" + getPort();
                else
                    msg = orig.getMessage() + " " +
                            getAddress().toString() + ":" + getPort();
                BindException be = new BindException(msg);
                be.initCause(orig);
                throw be;
            }
        }
        //if( serverTimeout >= 0 )
        //    serverSocket.setSoTimeout( serverTimeout );
        
        initialized = true;
        
    }
到这里Http11Protocol 协议处理器初始化已经完成,intit方面主要做了3件事情如下
  • 1连接器中接收socket连接的线程数量,
  • 2初始化ServerSocketFactory serverSocketFactory对象,该对象默认使用的DefaultServerSocketFactory类
  • 3根据端口号初始化ServerSocket对象
h.我们接着看Connector类的startInternal方法中的protocolHandler.start()的代码。代码如下
    public void start() throws Exception {
        if (this.domain != null) {
            try {
                tpOname = new ObjectName(domain + ":" + "type=ThreadPool,name=" + getName());
                Registry.getRegistry(null, null).registerComponent(endpoint, tpOname, null );
            } catch (Exception e) {
                log.error("Can't register endpoint");
            }
            rgOname=new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent( cHandler.global, rgOname, null );
        }

        try {
            endpoint.start();
        } catch (Exception ex) {
            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
            throw ex;
        }
        if (log.isInfoEnabled())
            log.info(sm.getString("http11protocol.start", getName()));
    }
主要就是调用了JIoEndpoint类的start()方法。继续跟进到JIoEndpoint类中的start方法代码。如下:
@Override
    public void start() throws Exception {
        // Initialize socket if not done before
        if (!initialized) {
            init();
        }
        if (!running) {
            running = true;
            paused = false;

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

            // Start acceptor threads
            for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(getDaemon());
                acceptorThread.start();
            }
        }
    }
主要完成两件事情
  • 调用JIoEndpoint类的父类AbstractEndpoint的createExecutor方法创建了一个线程池
  • 循环创建接受socket请求线程,前面我们看到JIoEndpoint在init方法中初始化是设置的为一个。所以这里只创建了一个线程。新建线程里面的
    Acceptor类实现了线程Runnable接口。Acceptor类也是JIoEndpoint类中的内部类。
解析分析Acceptor线程类的流程,看到这里似乎明白了tomcat客户端请求处理也是通过socket来实现的,是不是跟前面章节中的《tomcat原理解析(一):一个简单的实现》有点像。  代码如下:
protected class Acceptor implements Runnable {


        /**
         * The background thread that listens for incoming TCP/IP connections and
         * hands them off to an appropriate processor.
         */
        public void run() {

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

                // Loop if endpoint is paused
                while (paused) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                // Accept the next incoming connection from the server socket
                try {
                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);//通过serverSocketFacory来接收客户端socket请求,并拿到socket对象
                    serverSocketFactory.initSocket(socket);//socket初始化,奇怪里面怎么是空实现
                    // Hand this socket off to an appropriate processor
                    if (!processSocket(socket)) {//传入socket对象到SocketProcessor类中进行业务处理

                        // Close socket right away
                        try {
                            socket.close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }catch ( IOException x ) {
                    if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
                } catch (Throwable t) {
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }

                // The processor will recycle itself when it finishes

            }

        }

    }
到这里整个容器已经启动完成,并开启了ServerSocket监听前端socket的请求

三 总结

         在整个章节中都在描述tomcat的启动流程,其实最终目的就是要启动socket的监听,然后等待链接。对socket链接处理核心的过程都在JIoEndpoint类中,tomcat启动的过程跟初始化的处理非常相似,都是在父类LifecycleBase(具体类之前的关系图可以查看前面的《tomcat原理解析(二):整体架构》)中定义抽象的startInternal,然后由子类来具体实现,这就是使用抽象模版方法模式。所以学习和掌握设计模式能够有助于更好的理解源码。下面是我整理的一个序列图,展现了启动时的调用关系。下面章节开始分析浏览器客户端请求tomcat服务器如何找到想要的资源。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值