前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >tomcat-超详细的启动流程(start)

tomcat-超详细的启动流程(start)

作者头像
虞大大
发布2020-08-26 17:19:25
2.6K0
发布2020-08-26 17:19:25
举报
文章被收录于专栏:码云大作战码云大作战

一、Bootstrap.start()

(1)tomcat启动时会先调用脚本,在脚本运行时会启动Bootstrap的main方法,mian方法中会调用load方法进行初始化操作,通过责任链模式将所有结点初始化后,会调用start方法-该方法是tomcat启动的核心方法,即启动tomcat的核心线程。

在上一篇 - tomcat-超详细的启动流程(init)中提过这里的daemon实际上为catalina,因此调用catalina的start方法。

main方法中的源码如下:

代码语言:javascript
复制
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")) { //都会走进该if分支
        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 var4) {
    Throwable t = var4;
    if (var4 instanceof InvocationTargetException && var4.getCause() != null) {
        t = var4.getCause();
    }

    handleThrowable(t);
    t.printStackTrace();
    System.exit(1);
}

二、Catalina.start()

本质上与init()方法没啥区别,委派给下一个standardServer结点来进行start方法。

代码语言:javascript
复制
public void start() {

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

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

    long t1 = System.nanoTime();
    try {        //standardServer的start方法        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;
    }    //...    //在Bootstrap的main方法中 设置了为true,因此为调用await方法,保证主线程一直处于运行。
    if (await) {
        await();
        stop();
    }
}

这里的start方法也一样,由lifecycleBase来负责统一管理生命周期,并且子类会重新这个start方法,具体源码如下:

代码语言:javascript
复制
@Override
public final synchronized void start() throws LifecycleException {
    //...  上面不重要
    try {        //设置状态        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        //该方法由子类重写
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

找到standardServer对应的startInternal方法。

三、StandardServer.startInternal()

在StandardServer.startInternal()中会继续通过责任链模式向后传递,进行StandardService的start方法调用。

代码语言:javascript
复制
@Override
protected void startInternal() throws LifecycleException {
//触发监听fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
    globalNamingResources.start();
    
    //责任链继续向后传递,进行standardService的start方法    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
}//...
}

四、StandardService.startInternal()

从下图源码中可以看出,StandardService.startInternal(),先进行engine的start调用,再进行connector的start调用。

代码语言:javascript
复制
@Override
protected void startInternal() throws LifecycleException {

    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);
    // engine的start
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }

    //server.xml中无配置executors,所以不会执行这段代码    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

    mapperListener.start();
    //connector的start
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }
}

(1)StandardEngine.startInternal()

本质上调用了父类ContainerBase.startInternal()。核心源码如下,在代码中主要作用为将tomcat容器以线程池的方式来启动,这个startStopExecutor是在tomcat的init中被构造出来的。

代码语言:javascript
复制
@Override
protected synchronized void startInternal() throws LifecycleException {
    // 前面代码我跳过了,不重要
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }
代码语言:javascript
复制
    //...
}

tomcat容器的关系为:Engine——>Host——>Context——>Wrapper——>Servlet。

进入StartChild线程中可以看到线程实现方式为Callable,并且在child中又使用了start()。即Engine——>Host。

代码语言:javascript
复制
private static class StartChild implements Callable<Void> {

    private Container child;
    public StartChild(Container child) {
        this.child = child;
    }

    @Override
    public Void call() throws LifecycleException {
        child.start();
        return null;
    }
}

(2)StandardHost.startInternal()

StandardHost.startInternal()中又递归调用了父类ContainerBase.startInternal方法。即下一个线程池需要启动的子类为StandardContext。

代码语言:javascript
复制
@Override
protected synchronized void startInternal() throws LifecycleException {    // 前面代码...    super.startInternal();
}

(3)StandardContext.startInternal()

StandardContext.startInternal()这里的触发监听,比较核心,在内部会解析web.xml。

代码语言:javascript
复制
protected synchronized void startInternal() throws LifecycleException {
    // 前面代码...    try {             // 前面代码...            // 触发监听 - 解析web.xml
            fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
            // 这里child为Context的子类 Wrapper的start的调用
            for (Container child : findChildren()) {
                if (!child.getState().isAvailable()) {
                    child.start();
                }
}//后面代码...}

触发监听,核心代码:

代码语言:javascript
复制
@Override
public void lifecycleEvent(LifecycleEvent event) {
    // 前面代码...
    if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {        //触发监听的type为这一行        configureStart();
    } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
        beforeStart();
    } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
        if (originalDocBase != null) {
            context.setDocBase(originalDocBase);
        }
    } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
        configureStop();
    } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
        init();
    } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
        destroy();
    }

}
代码语言:javascript
复制
protected synchronized void configureStart() {
    //前面代码...
    webConfig();
    // 后面代码...
代码语言:javascript
复制
}
代码语言:javascript
复制
**
 * Scan the web.xml files that apply to the web application and merge them
 * using the rules defined in the spec. For the global web.xml files,
 * where there is duplicate configuration, the most specific level wins. ie
 * an application's web.xml takes precedence over the host level or global
 * web.xml file.
 *///看英文注释可以知道,这个方法是用来解析tomcat的web.xmlprotected void webConfig() {

在webConfig中也可以找到我们经常会配置的一些解析方法,比如servlet、filter等。

下图为filter的解析。

代码语言:javascript
复制
for (FilterDef filter : webxml.getFilters().values()) {
    if (filter.getAsyncSupported() == null) {
        filter.setAsyncSupported("false");
    }
    context.addFilterDef(filter);
}
for (FilterMap filterMap : webxml.getFilterMappings()) {
    context.addFilterMap(filterMap);
}

下图为servlet的解析。

代码语言:javascript
复制
for (ServletDef servlet : webxml.getServlets().values()) {
    Wrapper wrapper = context.createWrapper();
    if (servlet.getLoadOnStartup() != null) {
        wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
    }
    if (servlet.getEnabled() != null) {
        wrapper.setEnabled(servlet.getEnabled().booleanValue());
    }
wrapper.setName(servlet.getServletName());    // ...}

下图为listener的解析。

代码语言:javascript
复制
for (String listener : webxml.getListeners()) {
    context.addApplicationListener(listener);
}

(4)StandardWrapper.startInternal()

代码语言:javascript
复制
protected synchronized void startInternal() throws LifecycleException {

    //本质上也一样 调用父类的startInternal() 线程池启动
代码语言:javascript
复制
    super.startInternal();
    
}

这一步为止,tomcat容器(Engine—>Host—>Context—>Wrapper)启动完毕。

五、mapperListern.startInternal()

回到StandardService,会进行mapperListern的启动。找到子类重写的startInernal(),源码如下:

代码语言:javascript
复制
@Override
public void startInternal() throws LifecycleException {

setState(LifecycleState.STARTING);
    // 获取engine    Engine engine = service.getContainer();
    if (engine == null) {
        return;
    }

findDefaultHost();
    // 添加engine中的listerner    addListeners(engine);
    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            //遍历host-注册host
            registerHost(host);
        }
    }
}
代码语言:javascript
复制
private void registerHost(Host host) {

    String[] aliases = host.findAliases();
    mapper.addHost(host.getName(), aliases, host);
    for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {//注册host下的contextregisterContext((Context) container);
        }
    }
}
代码语言:javascript
复制
private void registerContext(Context context) {

    String contextPath = context.getPath();
    if ("/".equals(contextPath)) {
        contextPath = "";
    }
    Host host = (Host)context.getParent();
    WebResourceRoot resources = context.getResources();
    String[] welcomeFiles = context.findWelcomeFiles();
    List<WrapperMappingInfo> wrappers = new ArrayList<>();
    for (Container container : context.findChildren()) {        //遍历contxt-注册wrapper        prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
        if(log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerWrapper",
                    container.getName(), contextPath, service));
        }
    }
    //最后将host名+host端口+context+wrapper即servlet 添加到mapper中。
    mapper.addContextVersion(host.getName(), host, contextPath,
            context.getWebappVersion(), context, welcomeFiles, resources,
            wrappers);
    if(log.isDebugEnabled()) {
        log.debug(sm.getString("mapperListener.registerContext",
                contextPath, service));
    }
}

在mapper中会注册engine、host、context、wrapper,组成host名+host端口+context路径+context应用名+servlet添加到mapper中,因此当接受到http请求时,如果tomcat有多个工程,多个host情况下,可以根据url拆分然后然后根据mapper来进行匹配。

六、Connector.startInternal()

责任链模式委托protocolHandler.start(),实际上未NioEndpoint.startInternal()

代码语言:javascript
复制
protected void startInternal() throws LifecycleException {
    if (getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }

    setState(LifecycleState.STARTING);
    try {        //protocolHandler的启动        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

七、NioEndpoint.startInternal()

(1)在NioEndpoint.startInternal()中会创建工作线程池、clientPoller线程池、acceptor线程池。

代码语言:javascript
复制
public void startInternal() throws Exception {

    if (!running) {
        //...
        if ( getExecutor() == null ) {            //启动工作线程,            createExecutor();
        }

        initializeConnectionLatch();
        
        pollers = new Poller[getPollerThreadCount()];
        for (int i=0; i<pollers.length; i++) {
            pollers[i] = new Poller();
            Thread pollerThread       //启动clientPoller线程
       = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
        }
        //创建acceptor线程
        startAcceptorThreads();
    }
}

最小线程数量默认为10,最大线程数量默认为200,也可读取server.xml中配置的线程数量。

代码语言:javascript
复制
public void createExecutor() {
    internalExecutor = true;
    TaskQueue taskqueue = new TaskQueue();
    TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
    executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
    taskqueue.setParent( (ThreadPoolExecutor) executor);
}

(2)acceptor线程

在下面方法中会启动,acceptor线程,并创建acceptor对象。

代码语言:javascript
复制
protected void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new ArrayList<>(count);
    for (int i = 0; i < count; i++) {
        Acceptor<U> acceptor = new Acceptor<>(this);
        String threadName = getName() + "-Acceptor-" + i;
        acceptor.setThreadName(threadName);
        acceptors.add(acceptor);
        Thread t = new Thread(acceptor, threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

启动线程后,查询acceptor线程的run方法,发现acceptor中会建立socket连接,可以接收http请求。

代码语言:javascript
复制
@Override
public void run() {
    while (endpoint.isRunning()) {

        try {
            U socket = null;
            try {
                //接收socket
                socket = endpoint.serverSocketAccept();
            } catch (Exception ioe) {
               //...
            }
           
        } catch (Throwable t) {
           //...//...//...
代码语言:javascript
复制
        }
    }
}

到这一步tomcat完全启动成功,并且开启了对应的线程,可以接受http请求,处理http请求。

八、总结

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码云大作战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档