5

Tomcat 系列篇二-介绍下整体架构

 11 months ago
source link: https://nicksxs.me/2023/09/16/Tomcat-%E7%B3%BB%E5%88%97%E7%AF%87%E4%BA%8C-%E4%BB%8B%E7%BB%8D%E4%B8%8B-Engine/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Tomcat 系列篇二-介绍下整体架构

前面那一篇感觉上来的有点突兀,还是应该按照架构去慢慢解析,所以这里回归下我们整体的 Tomcat 架构,这里我们通过一个 Tomcat 的配置文件来看看

<Server>
    <Service>
        <Connector />
        <Connector />
        <Engine>
            <Host>
                <Context />
            </Host>
        </Engine>
    </Service>
</Server>

上次我们讲解了 connector,也提到了初始化的流程,在
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
代码中我们就能略窥一斑,

Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());

这里的 connector 是在 service 中的,而
tomcat.getService().addConnector(connector); 这一行
具体来看下

public Service getService() {
        return getServer().findServices()[0];
    }

又调用了,org.apache.catalina.startup.Tomcat#getServer

public Server getServer() {

        if (server != null) {
            return server;
        }

        System.setProperty("catalina.useNaming", "false");

        server = new StandardServer();

        initBaseDir();

        // Set configuration source
        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));

        server.setPort( -1 );

        Service service = new StandardService();
        service.setName("Tomcat");
        server.addService(service);
        return server;
    }

可以看到,先 new 了 StandardServer,再 new 了 StandardService,可以理解为创建 Server 后具体是由 service 进行服务,
而在 service 中就是上面的配置文件里显示的,service 包含了 connector,可以是多个connector,负责接入,具体内容可以参看上一篇
而后是 Engine,Engine 的关系是一个 Service 有一个 Engine,Engine 负责处理真正的逻辑,

public Engine getEngine() {
        Service service = getServer().findServices()[0];
        if (service.getContainer() != null) {
            return service.getContainer();
        }
        Engine engine = new StandardEngine();
        engine.setName( "Tomcat" );
        engine.setDefaultHost(hostname);
        engine.setRealm(createDefaultRealm());
        service.setContainer(engine);
        return engine;
    }

Engine 一般默认我们初始化的都是 StandardEngine,包括前面的 StandardServer 和StandardService,
而对于 host 来说,Engine 中可以包含多个 host,也就是可以处理多个虚拟主机的业务逻辑,

public Host getHost() {
        Engine engine = getEngine();
        if (engine.findChildren().length > 0) {
            return (Host) engine.findChildren()[0];
        }

        Host host = new StandardHost();
        host.setName(hostname);
        getEngine().addChild(host);
        return host;
    }

tomcat的特点也都是常规的懒加载,在 get 的第一次请求里进行初始化,这边同样创建了 StandardHost,对于可以有多个的 host,在 Engine 中添加也变成了addChild,而对于常规的 Tomcat 来说,往下一层就是 context 了,这个可以支持多个 web 应用,所以也是可以添加多个 context,但我这边以 springboot 嵌入的 Tomcat 举例,他是内嵌的 context

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = this.getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
        if (documentRoot != null) {
            context.setResources(new LoaderHidingResourceRoot(context));
        }

        context.setName(this.getContextPath());
        context.setDisplayName(this.getDisplayName());
        context.setPath(this.getContextPath());
        File docBase = documentRoot != null ? documentRoot : this.createTempDir("tomcat-docbase");
        context.setDocBase(docBase.getAbsolutePath());
        context.addLifecycleListener(new Tomcat.FixContextListener());
        context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader());
        this.resetDefaultLocaleMapping(context);
        this.addLocaleMappings(context);

        try {
            context.setCreateUploadTargets(true);
        } catch (NoSuchMethodError var8) {
        }

        this.configureTldPatterns(context);
        WebappLoader loader = new WebappLoader();
        loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
        loader.setDelegate(true);
        context.setLoader(loader);
        if (this.isRegisterDefaultServlet()) {
            this.addDefaultServlet(context);
        }

        if (this.shouldRegisterJspServlet()) {
            this.addJspServlet(context);
            this.addJasperInitializer(context);
        }

        context.addLifecycleListener(new StaticResourceConfigurer(context));
        ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers);
        host.addChild(context);
        this.configureContext(context, initializersToUse);
        this.postProcessContext(context);
    }

是一个 TomcatEmbeddedContext,这一点比较特殊,希望这样会有个一个大致的概念。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK