文档章节

Tomcat 源码分析(-)启动过程分析

AaronSheng
 AaronSheng
发布于 2016/11/28 19:40
字数 2012
阅读 148
收藏 4

    前面几篇文章分别介绍了Tomcat的安装、优化和框架,本文主要用于分析Tomcat源码启动过程,研究一个框架最好的着手方式可能就是研究它的启动过程,因为在这过程中我们可以看到它内部的层次关系,模块关系等,对整个框架有一个高层次的掌握,后续研究就可以得心应手了。
    研究Tomcat源码启动过程的最简单方式是可以通过Tomcat的启动类Bootstrap着手。

  1. Tomcat主要类分析
     
    1) Catalina
    Catalina负责Server的加载、启动和关闭,Bootstrap启动后将Server的加载、启动和关闭的操作全部委托到Catalina类。

    2) Server
    Server是Service的生存环境。一个Server中可以有多个Service。StandardServer是Server的标准实现类,StandardServer可以持有多个Service。

    2) Service
    Service将Connector和Container包装在一起提供对外服务,一个Service可以有多个Connector,但是只有一个Container。StandardService是Service的标准实现,StandardService分别持有多个Connector和一个Engine,将两者联系起来对外工作。

    3) Connector
    Connector负责对外交流。它的主要任务是负责接收浏览器的发过来的tcp连接请求,创建一个Request和Response对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的Request 和Response对象传给处理这个请求的线程,处理这个请求的线程就是Container组件要做的事了。
    ProtocolHandler负责选择底层运行模式Bio或者Nio,监听端口,创建线程以及数据的处理等,它是对AbstractEndPoint的轻度封装。Http11NioProtocol代表Nio模式Protocol,Tomcat8以后默认采用此协议。
    AbstractEndPoint代表具体的底层通讯,它负责运行模式Bio或者Nio,绑定端口,监听端口,端口的事件处理等关于Socket的事情。JIoEndPoint代表Bio模式,NioEndPoint代表Nio模式,Tomcat8以后默认都是使用Nio模式。

    4) Engine
    Engine是完整的容器,其下面拥有多个虚拟主机,它的责任就是将Connector请求分配给虚拟机处理。StandardEngine是Engine的标准实现类。

    5) Executor
    Executor工作线程,用于处理Connector传递的请求。
     
  2. Tomcat启动过程
    1) Bootstrap启动程序,Bootstrap是Tomcat的入口,负责加载和启动Server
    public static void main(String args[]) {
        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);
                // bootstrap加载Server
                daemon.load(args);
                // bootstrap启动Server
                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) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

    2) Bootstrap类load方法加载Server,Bootstrap加载Server最终委托给Catalina的load方法
    private void load(String[] arguments) throws Exception {
    
        // Call the load() method
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments == null || arguments.length == 0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        // Catalina load方法调用
        Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);
    }

    3) Catalina load方法负责加载Server
    public void load() {
        long t1 = System.nanoTime();
    
        initDirs();
        initNaming();
    
        // Create and execute our Digester
        Digester digester = createStartDigester();
    
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
                    inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail", getConfigFile()), e);
                    }
                }
            }
    
            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually.
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e);
                    }
                }
            }
    
            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]"));
                } else {
                    log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()));
                    if (file.exists() && !file.canRead()) {
                        log.warn("Permissions incorrect, read permission is not allowed on the file.");
                    }
                }
                return;
            }
    
            try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
                log.warn("Catalina.start using " + getConfigFile() + ": " +
                        spe.getMessage());
                return;
            } catch (Exception e) {
                log.warn("Catalina.start using " + getConfigFile() + ": " , e);
                return;
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    
        // 设置Server属性
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
        // Stream redirection
        initStreams();
    
        // Start the new server
        try {
            // Server初始化
            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("Catalina.start", e);
            }
        }
    
        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }
    }

    4) StandardServer初始化,StandardServer对应的多个Service也会被一一初始化
    protected void initInternal() throws LifecycleException {
        super.initInternal();
    
        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");
    
        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");
    
        // Register the naming resources
        globalNamingResources.init();
    
        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() && f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // 初始化所有的service
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

    5) StandardService初始化,StandardService分别初始化Engine, Executor和Connector
    protected void initInternal() throws LifecycleException {
        super.initInternal();
    
        // 初始化Engine
        if (engine != null) {
            engine.init();
        }
    
        // 初始化所有的Executor
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }
    
        // Initialize mapper listener
        mapperListener.init();
    
        // 初始化所有的Connector
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString("standardService.connector.initFailed", connector);
                    log.error(message, e);
    
                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

    6) Engine初始化
    protected void initInternal() throws LifecycleException {
        // Ensure that a Realm is present before any attempt is made to start
        // one. This will create the default NullRealm if necessary.
        getRealm();
        super.initInternal();
    }

    7) Connector初始化
    protected void initInternal() throws LifecycleException {
    
        super.initInternal();
    
        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
    
        // Make sure parseBodyMethodsSet has a default
        if( null == parseBodyMethodsSet ) {
            setParseBodyMethods(getParseBodyMethods());
        }
    
        if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr", getProtocolHandlerClassName()));
        }
        if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() && protocolHandler instanceof AbstractHttp11JsseProtocol) {
            AbstractHttp11JsseProtocol<?> jsseProtocolHandler = (AbstractHttp11JsseProtocol<?>) protocolHandler;
            if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) {
                // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
                jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
            }
        }
    
        try {
            // 初始化Protocol,Bio或者Nio,开启监听端口
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

    8) Bootstrap start方法启动Server,Bootstrap启动Server最终也委托给Catalina的start方法
    public void start() throws Exception {
        if (catalinaDaemon == null) init();
    
        // Cataline start方法调用
        Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
        method.invoke(catalinaDaemon, (Object[]) null);
    
    }

    9) Catalina start方法启动Server
    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();
        } 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");
        }
    
        // 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();
        }
    }

    10) StandardServer启动
    protected void startInternal() throws LifecycleException {
    
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
    
        globalNamingResources.start();
    
        // 启动所有的Service
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

    11) StandardService启动
    protected void startInternal() throws LifecycleException {
        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);
    
        // 启动Engine
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }
    
        // 启动所有的Executor
        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }
    
        mapperListener.start();
    
        // 启动所有的Connector
        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);
                }
            }
        }
    }

    12) StandardEngine启动
    protected synchronized void startInternal() throws LifecycleException {
    
        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
    
        // Standard container startup
        super.startInternal();
    }

    13) Connector启动
    protected void startInternal() throws LifecycleException {
    
        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }
    
        setState(LifecycleState.STARTING);
    
        try {
            // 启动Protocol, Bio或者Nio,开始接受请求
            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);
        }
    }

     
  3. 总结
    从Tomcat启动过程来看,Bootstrap是Tomcat的启动入口,而Tomcat只是对Catalina的调用,所有的加载、启动和关闭操作最终都由Catalina完成,Catalina负责控制Server的启动、加载和关闭,Server的加载、启动和关闭内部都是依次对Engine, Executor和Connector的调用。通过Tomcat启动过程,我们可以清楚地了解Tomcat内部架构,后面可以更好地了解Tomcat内部机制。

© 著作权归作者所有

AaronSheng
粉丝 14
博文 52
码字总数 51122
作品 0
深圳
程序员
私信 提问
揭开tomcat神秘的面纱之bootstrap启动2

在上文揭开tomcat神秘的面纱之bootstrap加载1中,本菜鸟分析道了context的启动就嘎然而止,是因为的启动过程最复杂,现在来分析context的启动过程。 是一个webApp,所有相关Context的启动都在...

一滴水的坚持
2018/11/25
0
0
Tomcat源码分析

下面谈谈我对Tomcat架构的理解 总体架构: 1、面向组件架构 2、基于JMX 3、事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成,如Server、...

五大三粗
2015/09/28
177
0
谈谈 Tomcat 架构及启动过程[含部署]

谈谈 Tomcat 架构及启动过程[含部署] ImportNew2018-01-061 阅读 Tomcat 原文出处: Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读...

ImportNew
2018/01/06
0
0
Springboot 随笔(1) -- 自动引入配置与启动机制

为什么用SpringBoot? 同上题记。总结:快速开始,方便搭建,开发web时并不需要Tomcat或者Jetty,甚至连插件都不用(因为自带Tomcat或自配置成Jetty)。 肯定有缺点吧? 一个框架除了知道他的...

alexqdjay
2016/10/09
605
0
谈谈 Tomcat 架构及启动过程[含部署]

原文出处:Rainstorm 这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读过源码以后写的总结,在写的过程中可能会忽略一些前提条件,如果有哪些比较...

Rainstorm
2018/01/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

centos 查看删除旧内核

1、查看系统中安装的内核 $ yum list installed | grep kernel 2、删除系统中旧内核 $ yum install yum-utils$ package-cleanup --oldkernels --count=2...

编程老陆
48分钟前
7
0
ES6

ES6:不改变原理的基础上,让API变得更简单 一、let:代替var用于声明变量 1、var的缺点: (1)声明提前 (2)没有块级作用域 2、let的优点: (1)组织了申明提前 (2)让let所在的块({}),...

wytao1995
今天
3
0
kubernetes 环境搭建 —— minikube

创建集群 minikube start 搭建好 k8s 集群后,可以查看集群的状态以及部署应用。主要用到的是 k8s 的 api,这通常需借助于 kutectl 命令行工具 基本操作 kubectl versionkubectl cluster-i...

lemos
今天
9
0
关于js混淆与反混淆还原操作

使用js的混淆加密,其目的是为了保护我们的前端代码逻辑,对应一些搞技术吃饭的公司来说,为了防止被竞争对手抓取或使用自己的代码,就会考虑如何加密,或者混淆js来达到代码保护。 1、为什么...

开源oschina
今天
11
0
用盛金公式解三次方程(ansi c版)

/* cc cubic.c -lm gcc cubic.c -lm Shengjin's Formulas Univariate cubic equation aX ^ 3 + bX ^ 2 + cX + d = 0, (a, b, c, d < R, and a!= 0). Multiple root disc......

wangxuwei
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部