Tomcat原理简析--服务器启动

作者: Mr_Puff | 来源:发表于2017-08-24 14:01 被阅读174次

    基本组件

    Tomcat顶层容器是Server,即一个应用服务器,对应于PC机的一个主进程。Server中包含一个或多个Service,用于提供具体的应用服务。Service主要包含Container和Connector两部分,Container内包装了具体的Web应用和服务,Connector主要处理请求连接相关的内容。
    主要的组件类图:


    Container.png

    Tomcat启动过程

    Boostrap类是tomcat启动的入口,启动tomcat实际上是调用了这个类的main方法:

    public final class Bootstrap {
    ...
    public static void main(String args[]) {
    
            if (daemon == null) {
                // 新建一个Bootstrap
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
            //解析命令行参数
            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();
                } else if (command.equals("start")) {
                    daemon.setAwait(true);
                    daemon.load(args);
                    daemon.start();
                } 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.");
                }
            } catch (Throwable t) {          
                if (t instanceof InvocationTargetException &&
                        t.getCause() != null) {
                    t = t.getCause();
                }
                handleThrowable(t);
                t.printStackTrace();
                System.exit(1);
            }
    
        }
    ...
    }
    

    整个过程并不复杂,首先新建一个Bootstrap实例并通过init方法来完成Catalina类的初始化,然后对命令行参数进行处理,默认执行start命令。start命令的处理主要通过三个方法来完成:setAwait(true), load(args), start()。这三个方法分别通过反射的方式来调用已经初始化的Catalina实例,实际上整个启动过程是由Catalina完成的。

    • Catalina的启动过程

    Catalina的启动过程主要通过三个方法来完成:

    public class Catalina {
        public void setAwait(boolean b) {
            await = b;
        }
    
        public void load() {
            long t1 = System.nanoTime();    
            // 创建server过程省略,主要是通过Digester读取server.xml配置文件,完成一些属性赋值
            getServer().setCatalina(this);   
            try {
                getServer().init();//调用Server的init加载所有Service
            } catch (LifecycleException e) {
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new java.lang.Error(e);
                } else {
                    log.error("Catalina.start", e);
                }
    
            }
    
            long t2 = System.nanoTime();
            if(log.isInfoEnabled()) {
                log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
            }
    
        }
    
        public void start() {
            if (getServer() == null) {
                load();
            }
            if (getServer() == null) {
                log.fatal("Cannot start server. Server instance is not configured.");
                return;
            }
    
            long t1 = System.nanoTime();
            //启动Server实例
            try {
                getServer().start();//调用Server的start方法,启动所有加载的Service
            } 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;
            }
    
            long t2 = System.nanoTime();
            if(log.isInfoEnabled()) {
                log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
            }
    
            //  省略注册关闭钩子代码    
            if (await) {
                await();
                stop();
            }
        }
    }
    

    load方法完成对配置文件的解析和server实例属性设置,然后通过start方法调用Server的start方法。Server被定义为一个接口,包含具体的服务管理方法。最后,通过Server的await方法来启动一个监听循环,当这个循环退出时stop方法才会被调用,从而停止服务器。

    • Server的启动过程

    Server接口定义了一系列的Service管理和服务器生命周期管理方法,Server默认实现类是StandardServer, 这个类继承自LifecycleBase,在init的时候调用了initInternal方法,start的时候调用startInternal方法,并且这两个模板方法被StandardServer重写:

    public final class StandardServer extends LifecycleMBeanBase implements Server {
        @Override
        protected void initInternal() throws LifecycleException {
             ...
            // Initialize our defined Services
            for (int i = 0; i < services.length; i++) {
                services[i].init();
            }
        }
    
        @Override
        protected void startInternal() throws LifecycleException {
            ...
            // Start our defined Services
            synchronized (servicesLock) {
                for (int i = 0; i < services.length; i++) {
                    services[i].start();
                }
            }
        
    }
    

    通过以上两个调用就完成了所有Service的初始化和启动,随后通过await方法来使服务器进入等待状态:

    @Override
        public void await() {
            if( port == -2 ) {
                //直接退出.
                return;
            }
            if( port==-1 ) {
                try {
                    awaitThread = Thread.currentThread();
                    while(!stopAwait) {
                        try {
                            Thread.sleep( 10000 );
                        } catch( InterruptedException ex ) {
                            // 无法通过8005端口命令退出
                        }
                    }
                } finally {
                    awaitThread = null;
                }
                return;
            }
    
            // Set up a server socket to wait on
            try {
                awaitSocket = new ServerSocket(port, 1,
                        InetAddress.getByName(address));
            } catch (IOException e) {
                log.error("StandardServer.await: create[" + address
                                   + ":" + port
                                   + "]: ", e);
                return;
            }
    
            try {
                awaitThread = Thread.currentThread();
    
                // Loop waiting for a connection and a valid command
                while (!stopAwait) {
                    ServerSocket serverSocket = awaitSocket;
                    if (serverSocket == null) {
                        break;
                    }
        
                    // Wait for the next connection
                    Socket socket = null;
                    StringBuilder command = new StringBuilder();
                    try {
                        InputStream stream;
                        long acceptStartTime = System.currentTimeMillis();
                        try {
                            socket = serverSocket.accept();
                            socket.setSoTimeout(10 * 1000);  // Ten seconds
                            stream = socket.getInputStream();
                        } catch (SocketTimeoutException ste) {
                            log.warn(sm.getString("standardServer.accept.timeout",
                                    Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
                            continue;
                        } catch (AccessControlException ace) {
                            log.warn("StandardServer.accept security exception: "
                                    + ace.getMessage(), ace);
                            continue;
                        } catch (IOException e) {
                            if (stopAwait) {
                                // Wait was aborted with socket.close()
                                break;
                            }
                            log.error("StandardServer.await: accept: ", e);
                            break;
                        }    
                    ...          
                    // Match against our command string
                    boolean match = command.toString().equals(shutdown);
                    if (match) {
                        log.info(sm.getString("standardServer.shutdownViaPort"));
                        break;
                    } else
                        log.warn("StandardServer.await: Invalid command '"
                                + command.toString() + "' received");
                }
            } finally {
                ServerSocket serverSocket = awaitSocket;
                awaitThread = null;
                awaitSocket = null;
    
                // Close the server socket and return
                if (serverSocket != null) {
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        // Ignore
                    }
                }
            }
        }
    

    通过一个while循环来建立ServerSocket监听客户端连接,同时会在该端口监听客户端的shutdown命令,主要配置是在conf/server.xml文件中:

    <Server port="8005" shutdown="SHUTDOWN">
    ...
    </Server>
    

    通过设置这个端口为-1即可禁用网络命令停止tomcat服务。

    • Service的启动过程

    Service是开发者部署在tomcat上的web应用抽象概念,这个接口的默认实现是StandardService,也继承自LifestyleBase类,相关的初始化和加载主要通过调用initInternal和startInternal来完成:

    public class StandardService extends LifecycleMBeanBase implements Service {
        @Override
        protected void initInternal() throws LifecycleException {
    
            super.initInternal();
            
            if (container != null) {
                container.init();
            }
    
            // Initialize any Executors
            for (Executor executor : findExecutors()) {
                if (executor instanceof LifecycleMBeanBase) {
                    ((LifecycleMBeanBase) executor).setDomain(getDomain());
                }
                executor.init();
            }
    
            // Initialize our defined Connectors
            synchronized (connectorsLock) {
                for (Connector connector : connectors) {
                        connector.init();
                }
            }
        }
    
        @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 (container != null) {
                synchronized (container) {
                    container.start();
                }
            }
    
            synchronized (executors) {
                for (Executor executor: executors) {
                    executor.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);
                    }
                }
            }
        }
    }
    

    主要调用了container,executors和connectors的init和start来完成相关应用组件的加载,executors是在connector中管理的线程池,可以通过server.xml进行配置,默认是注释的:

    <Service name="Catalina">
    
        <!--The connectors can use a shared executor, you can define one or more named thread pools-->
        <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
            maxThreads="150" minSpareThreads="4"/>
    
        <Connector executor="tomcatThreadPool"
                port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
    </Service>
    

    通过以上配置就完成了connector的线城池配置。整个启动过程至此就完成了,主要包括init和start两个阶段,分别完成各个组件的初始化和启动过程,如下图:

    startup.png

    相关文章

      网友评论

        本文标题:Tomcat原理简析--服务器启动

        本文链接:https://www.haomeiwen.com/subject/dnbsdxtx.html