美文网首页
Tomcat 启动初始化

Tomcat 启动初始化

作者: 绝尘驹 | 来源:发表于2019-06-09 23:18 被阅读0次

tomcat 启动类

我们通过tomcat的catalina.sh 的脚本启动tomcat时,执行的是tomcat的Bootstrap类的main方法。这是tomcat的入口。
代码如下:

public static void main(String args[]) {

        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    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 {
            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();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } 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) {
            // 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的启动需要先执行Bootstrap的初始化init()后,再执行load,start核心方法,我们下线一次分析这个这三个方法

初始化Catalina

代码如下:

  public void init() throws Exception {

    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");
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

    // Set the shared extensions class loader
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    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);

    catalinaDaemon = startupInstance;
}

主要是创建common-classloader,serverClassLoader都是用这个类,同时用common-classloader加载Catalina这个类,致于为啥要用classloader来加载这个类,个人觉得是安全,因为这个common-classloader只会加载jdk以及tomcat自己lib目录下的jar包,而不会加载其他目录下的jar。

Load 部分分析

load方法tomcat也是通过反射调用的Catalina 的load()方法,这里就开始解析tomcat的核心配置文件server.xml的地方

public void load() {

    if (loaded) {
        return;
    }
    loaded = true;

    long t1 = System.nanoTime();

    initDirs();

    // Before digester - it may be needed
    initNaming();

    // Set configuration source
    ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
    File file = configFile();

    // Create and execute our Digester
    // 解析server.xml配置文件
    Digester digester = createStartDigester();

    try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
        InputStream inputStream = resource.getInputStream();
        InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
        inputSource.setByteStream(inputStream);
        digester.push(this);
        digester.parse(inputSource);
    } catch (Exception e) {
        log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
        if (file.exists() && !file.canRead()) {
            log.warn(sm.getString("catalina.incorrectPermissions"));
        }
        return;
    }

    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection
    initStreams();

    // Start the new server
    try {
        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);
        }
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
    }
}

tomcat通过Digester来解析server.xml配置文件,并创建sever对应的StandardServer,service对应的是StandardService,在初始化server时,会把service全部初始化完,包含engin,Connector等。

这里主要说下connector,tomcat默认的connector是http1.1

public Connector(String protocol) {
    boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();

    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
        }
    } else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
        }
    } else {
        protocolHandlerClassName = protocol;
    }

    // Instantiate protocol handler
    ProtocolHandler p = null;
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    } finally {
        this.protocolHandler = p;
    }

    // Default for Connector depends on this system property
    setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}

Start

tomcat 初始化完后,就开始启动tomcat的nio线程,catalina的工作线程。

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

    if (getServer() == null) {
        load();
    }

    if (getServer() == null) {
        log.fatal(sm.getString("catalina.noServer"));
        return;
    }

    long t1 = System.nanoTime();

    // Start the new server
    try {
        //开始启动服务,是server.xml文件配置的server,执行的是
       //StandardServer的startInternal()方法。
        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;
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
    }

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

StandardServer的startInternal 方法如下:

 // Start our defined Services
  synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            //services是server.xml配置文件里的service,实现是StandardService,同理也是执行的startInternal()方法。
            services[i].start();
        }
  }

StandardService的startInternal 是核心,catalina的引擎engin,nio线程都在这里启动

@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.start();
        }
    }
    
   //catalina worker 线程池创建,Executor 是由StandardThreadExecutor实现的,具体也是由StandardThreadExecutor的startInternal方法实现。
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

    //这里是tomcat context path路由相关的,后面单独一偏分析
    mapperListener.start();

    // Start our defined Connectors second
    // 初始化nio select和poll线程
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }
}

tomcat工作线程池创建由StandardThreadExecutor的startInternal方法实现,具体实现如下:

protected void startInternal() throws LifecycleException {
    //TaskQueue是tomcat的LinkedBlockingQueue,重写了offer等方法
    taskqueue = new TaskQueue(maxQueueSize);
    TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
    executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
    executor.setThreadRenewalDelay(threadRenewalDelay);
    if (prestartminSpareThreads) {
        executor.prestartAllCoreThreads();
    }
    taskqueue.setParent(executor);

    setState(LifecycleState.STARTING);
}

tomcat的nio线程创建和启动connector负责实现,后面单独开一偏。

相关文章

  • Tomcat 监听初始化流程

    开篇  这篇博文的主要目的是为了理清楚Tomcat监听的初始化流程,所谓的监听初始化流程是指Tomcat启动后直至...

  • Tomcat 监听初始化流程

    开篇  这篇博文的主要目的是为了理清楚Tomcat监听的初始化流程,所谓的监听初始化流程是指Tomcat启动后直至...

  • Tomcat 启动初始化

    tomcat 启动类 我们通过tomcat的catalina.sh 的脚本启动tomcat时,执行的是tomcat...

  • Tomcat nio 网络初始化

    上一偏文章写tomcat的启动初始化,包含tomcat的init,load,start的三个核心部分,讲了tomc...

  • tomcat和undertow对比实践

    1、初始化启动服务器时,对比图 tomcat: upload-images.jianshu.io/upload_i...

  • Spring-Boot--容器初始化动态加载(@Conditio

    spring boot集成了jetty,tomcat,undertow等WEB容器,启动时可以智能的选择初始化哪个...

  • tomcat原理分析

    Tomcat8源码分析系列-启动分析(二) Catalina初始化 转载自 https://blog.csdn.n...

  • tomcat优化

    tomcat内存优化 Tomcat内存优化主要是对 tomcat 启动参数优化,我们可以在 tomcat 的启动脚...

  • tomcat-4-Lifecycle

    什么是Lifecycle 组件tomcat启动时初始化依赖的下层组件父组件管理子组件 Servlet加载和实例化(...

  • tomcat-5-Pipeline

    什么是Lifecycle 组件tomcat启动时初始化依赖的下层组件父组件管理子组件 Servlet加载和实例化(...

网友评论

      本文标题:Tomcat 启动初始化

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