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

来源:互联网 发布:淘宝百度文库财富值 编辑:程序博客网 时间:2024/05/21 09:06

一 概述

 好久没有继续更新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服务器如何找到想要的资源。







0 0
原创粉丝点击