博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomcat 源码分析(-)启动过程分析
阅读量:6243 次
发布时间:2019-06-22

本文共 16268 字,大约阅读时间需要 54 分钟。

hot3.png

    前面几篇文章分别介绍了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内部机制。

转载于:https://my.oschina.net/u/2950586/blog/795686

你可能感兴趣的文章
高危漏洞预警:WordPress Core 多个高危漏洞
查看>>
《DNS与BIND(第5版)》——1.5 一定要使用DNS吗
查看>>
"挖掘机指数"告诉你不一样的中国经济
查看>>
看麦肯锡如何分析中国城市群
查看>>
《数据分析变革:大数据时代精准决策之道》一1.4 全面看待运营型分析
查看>>
一分钟自我介绍:阿里云CDN
查看>>
《iOS 8开发指南》——第6章,第6.5节实战演练——使用模板Single View Application...
查看>>
【观点】离开了信息化,大数据就是为他人作嫁衣
查看>>
《HTML5+CSS3网页设计入门必读》——1.4 分裂:WHATWG TF
查看>>
《JavaScript核心概念及实践》——第2章 基本概念 2.1 数据类型
查看>>
Linux有问必答:如何修复"fatal error: jsoncpp/json/json.h: No such file..."
查看>>
阿里数据库内核月报:2016年11月
查看>>
简单了解Disruptor(一)
查看>>
编写更好 Bash 脚本的 8 个建议
查看>>
Mavens实战 1.5小结
查看>>
《 硬件创业:从产品创意到成熟企业的成功路线图》——第1章 硬件创业概述 1.1 早期的创客们...
查看>>
《Android游戏开发详解》——第3章,第3.5节继承
查看>>
《Docker生产环境实践指南》——2.6 编排
查看>>
Docker学习(一)
查看>>
云端架美购,精品零距离
查看>>