唯爱Java服务器端编程首页投稿(暂停使用,暂停投稿)

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

2017-08-24  本文已影响174人  Mr_Puff

基本组件

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的启动过程主要通过三个方法来完成:

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接口定义了一系列的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是开发者部署在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
上一篇下一篇

猜你喜欢

热点阅读