深入剖析Tomcat(十三) Host、Engine 容器

news2024/10/6 0:30:29

前面很多篇文章都在介绍Context与Wrapper两个容器,因为这两个容器确实也比较重要,与我们日常开发也息息相关,但是Catalina是存在四个容器的,这一章就来简单看看Host与Engine这两个容器。

再次展示下Catalina的容器结构,Engine为最顶层的容器,父容器与子容器为一对多的关系,最底层的容器为Wrapper容器,Wrapper容器不再拥有子容器。

Context容器代表一个Web应用,Wrapper容器代表Web应用中的一个servlet,那么Host与Engine代表啥呢,下面通过梳理Engine、Host与Context三者的关系来说明一下。

Host容器与Context容器

一个Tomcat中可以布有多个Web应用,也就是可以有多个Context容器实例。至于如何将请求分发到指定Context中,则是Host容器的任务了。StandardHost类是Host容器的标准实现。

下面通过一张图来展示下Host容器寻址Context容器的过程

根据uri去匹配Context容器的逻辑是一个循环判断的逻辑,代码在StandardHost#map() 方法中,下面是精简后的代码

public Context map(String uri) {

    Context context = null;
    String mapuri = uri;
    while (true) {
        context = (Context) findChild(mapuri);
        if (context != null) {
            break;
        }
        int slash = mapuri.lastIndexOf('/');
        if (slash < 0) {
            break;
        }
        mapuri = mapuri.substring(0, slash);
    }

    // 如果没有匹配上就用默认的Context
    if (context == null) {
        context = (Context) findChild("");
    }

    return (context);

}

假如请求的uri是【/app1/Primitive】,那么拿uri去匹配Context的步骤是这样的

  1. 先拿“/app1/Primitive”去匹配,没有匹配上
  2. 去掉最后的一个“/”后面的内容,uri变为“/app1”,再去匹配,匹配上了,拿到结果

如果最终也没有匹配上,就取默认的Context来用,默认Context在children中的key为空字符串。像我们使用SpringBoot开发项目的话,项目针对的Context在Host children中的key就为空字符串。

书中源码给了一个以Host为顶级容器的示例,我将启动类展示出来,感兴趣的可以下源码来自己试试

package ex13.pyrmont.startup;

//explain Host

import ex13.pyrmont.core.SimpleContextConfig;
import org.apache.catalina.*;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader;


public final class Bootstrap1 {
    public static void main(String[] args) {
        //invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern
        System.setProperty("catalina.base", System.getProperty("user.dir"));
        Connector connector = new HttpConnector();

        Wrapper wrapper1 = new StandardWrapper();
        wrapper1.setName("Primitive");
        wrapper1.setServletClass("PrimitiveServlet");
        Wrapper wrapper2 = new StandardWrapper();
        wrapper2.setName("Modern");
        wrapper2.setServletClass("ModernServlet");

        Context context = new StandardContext();
        // StandardContext's start method adds a default mapper
        context.setPath("/app1");
        context.setDocBase("app1");

        context.addChild(wrapper1);
        context.addChild(wrapper2);

        LifecycleListener listener = new SimpleContextConfig();
        ((Lifecycle) context).addLifecycleListener(listener);

        Host host = new StandardHost();
        host.addChild(context);
        host.setName("localhost");
        host.setAppBase("webapps");

        Loader loader = new WebappLoader();
        context.setLoader(loader);
        // context.addServletMapping(pattern, name);
        context.addServletMapping("/Primitive", "Primitive");
        context.addServletMapping("/Modern", "Modern");

        connector.setContainer(host);
        try {
            connector.initialize();
            ((Lifecycle) connector).start();
            ((Lifecycle) host).start();

            // make the application wait until we press a key.
            System.in.read();
            ((Lifecycle) host).stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Engine容器与Host容器

host,顾名思义,url中的host可以是域名也可以是ip。Host容器对应的也就是url中的host,每个Host容器实例都被安排接收其对应的“url中带有相关host的请求”。至于如何将一个请求分发到指定Host容器实例中,则是Engine容器的任务了,Engine容器是顶层容器,它不会再有父容器。StandardEngine类是Engine容器的标准实现。

如果Engine容器作为顶层容器的话,连接器则与Engine容器做关联,一个请求进来后,连接器则会将请求抛入Engine的invoke()方法,Engine再将请求给到指定的Host,Host再给到指定的Context,Context再给到指定的Wrapper,层层流转下来,请求就到达了指定的servlet中,servlet会对请求进行处理与反馈,然后将反馈结果层层上抛,最终给到连接器,连接器将结果返回给客户端,一个请求的寻址与处理流程就完成了。

我们通过一张图来展示下Engine容器寻址Host容器的过程

简单来说就是解析请求url中的host字符串,然后拿这个host字符串当做Host实例的name去 子容器map集合 "children" 中去匹配Host容器实例,匹配不上的话就用默认Host,默认Host一般为key=localhost 的这个Host。用SpringBoot开发的项目中就只有一个 key为 localhsot 的Host实例,默认Host也是它。

书中源码给了一个以Engine为顶级容器的示例,我将启动类展示出来,感兴趣的可以下源码来自己试试

package ex13.pyrmont.startup;

//Use engine

import ex13.pyrmont.core.SimpleContextConfig;
import org.apache.catalina.*;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader;


public final class Bootstrap2 {
    public static void main(String[] args) {
        //invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern
        System.setProperty("catalina.base", System.getProperty("user.dir"));
        Connector connector = new HttpConnector();

        Wrapper wrapper1 = new StandardWrapper();
        wrapper1.setName("Primitive");
        wrapper1.setServletClass("PrimitiveServlet");
        Wrapper wrapper2 = new StandardWrapper();
        wrapper2.setName("Modern");
        wrapper2.setServletClass("ModernServlet");

        Context context = new StandardContext();
        // StandardContext's start method adds a default mapper
        context.setPath("/app1");
        context.setDocBase("app1");

        context.addChild(wrapper1);
        context.addChild(wrapper2);

        LifecycleListener listener = new SimpleContextConfig();
        ((Lifecycle) context).addLifecycleListener(listener);

        Host host = new StandardHost();
        host.addChild(context);
        host.setName("localhost");
        host.setAppBase("webapps");

        Loader loader = new WebappLoader();
        context.setLoader(loader);
        // context.addServletMapping(pattern, name);
        context.addServletMapping("/Primitive", "Primitive");
        context.addServletMapping("/Modern", "Modern");

        Engine engine = new StandardEngine();
        engine.addChild(host);
        engine.setDefaultHost("localhost");

        connector.setContainer(engine);
        try {
            connector.initialize();
            ((Lifecycle) connector).start();
            ((Lifecycle) engine).start();

            // make the application wait until we press a key.
            System.in.read();
            ((Lifecycle) engine).stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Engine与Host 两个容器的主要内容就是上面这些,涉及到的类也是容器老生常谈的几种类,容器标准实现类 StandardEngine,StandardHost;容器Pipeline的基础阀 StandardEngineValve,StandardHostValve;容器相关的映射器 StandardEngineMapper,StandardHostMapper,这些映射器在Tomcat4中还存在,现在使用的Tomcat版本已经不存在这些映射器了,根据请求获取Host、Context的这些逻辑都放在了request对象中来实现了。想看看源码的看看这几个类就行。

本章内容就介绍到这里,Engine与Host两个容器的功能与我们的开发工作 关联并不太多,我们了解其工作方式即可。

源码分享

https://gitee.com/huo-ming-lu/HowTomcatWorks

这一篇在源码中有两个示例,感兴趣的同学可以自己运行看看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1869330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

unittest接口测试框架设计

1、测试框架的价值 1、实现对所有测试脚本、测试数据文件以及测试报告文件的管理,提升接口测试执行和回归的效率。 2、降低人工的工作成本&#xff0c;可以在下班之后通过框架来自动运行&#xff0c;提高单位人工的产能。 3、框架可以应对多种不同的接口测试工作的需求&…

今天不看明天付费------中国AGI(人工智能)的发展趋势

深入解析了中国AGI&#xff08;人工智能&#xff09;的发展趋势&#xff0c;并清晰地展示了其市场分层结构。 ** 从下至上&#xff0c;AGI市场被划分为四个主要层级&#xff1a;基础设施层、模型层、中间层和应用层。 基础设施层作为最底层&#xff0c;为AGI的发展提供了坚实…

Gartner发布2024年企业高管增长议程:使网络安全投资与业务增长保持一致

网络安全投资和准备被视为推动企业发展的关键因素。除了避免损失之外&#xff0c;高管还应利用有效的以业务为中心的安全方法&#xff0c;通过大规模实现敏捷性和创新来推动收入增长。 主要发现 高增长公司通过扩大商业足迹来推动业绩&#xff0c;这需要大规模的创新、敏捷性和…

【论文解读】通过多标记预测建立更好更快的大型语言模型

Meta 的这篇多标记预测论文显示,与当前的下一标记预测器相比,多头预测器内存效率高、性能更好、训练速度更快。 https://arxiv.org/pdf/2404.19737 主要收获: 多标记预测是对 LLM 训练的一种简单而强大的修改,可提高样本效率和各种任务的性能。这种方法在大规模应用中尤为…

Linux编程---给函数取别名

0 Preface/Foreword 1 代码 1.1 源代码 #include <stdio.h> #include <string.h> int sum(int a, int b);int sum_alias(int a, int b) __attribute__ ((alias("sum"))); int main() { int ret 0; ret sum(5, 5) sum_alias(5, 5); …

学习笔记——动态路由——RIP(RIP路由汇总介绍)

四、RIP路由汇总介绍 当网络中路由器的路由条目非常多时&#xff0c;可以通过路由汇总&#xff08;又称路由汇聚或路由聚合&#xff09;来减少路由条目数&#xff0c;加快路由收敛时间和增强网络稳定性。 路由汇总的原理是&#xff0c;同一个自然网段内的不同子网的路由在向外…

开关电源调试记录-基于DK112(DK1203也一样)作为开关主控芯片的开关电源

调试了一款DK112&#xff08;datasheet&#xff09;开关电源控制芯片。 1、原理图如下&#xff1a; 2、测试波形 a.输出波形&#xff0c;图中标识“5V”的位置 b.芯片VCC引脚&#xff0c;图中标识“4”的位置 c.芯片FB引脚&#xff0c;图中标识“3”的位置 对于FB引脚&…

防近视台灯有效果吗?专业护眼台灯推荐!告诉你台灯怎么选

随着学业负担的加重和电子设备的广泛普及&#xff0c;近视问题在青少年群体中愈发凸显&#xff0c;近视率持续走高。导致近视的因素错综复杂&#xff0c;除了过度使用手机外&#xff0c;遗传因素、不良的用眼习惯、环境因素、营养不均衡以及学习压力等均为重要因素&#xff0c;…

VBA 进度条(2)

1.前提 1-1. 在VBA编辑器找到工具-引用-勾选MicroSoft Visual Basic for Applications Extensibility Library 1-2. 信任中心 -> 宏设置 -> 开发人员宏设置 -> 选中“信任对VBA工程对象模型的访问” 2.类模块 Private objApp As Object Private u…

国行版苹果Vision Pro即将发售 高昂定价吓退普通消费者?

2024年2月2日&#xff0c;苹果第一代空间计算设备Vision Pro在美国上市。6月28日&#xff0c;国行版苹果Vision Pro也将正式发售&#xff0c;别为256GB版29999元、512GB版31499元、1TB版32999元。不过从此前Vision Pro预售情况来看&#xff0c;Vision Pro的“杀手锏”在“价格”…

基于JSP的书店仓库管理系统

开头语&#xff1a;你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;JSP 数据库&#xff1a;MySQL 技术&#xff1a;JSPJava 工具&#xff1a;ECLIPSE、Tomcat 系统展示 首页 管理员功能模块…

简过网:上万元的学费,考公到底要不要报个培训班?

考公报不报班一直是很多朋友比较纠结一件事&#xff0c;报班了学费太贵&#xff0c;不报班又怕考不上&#xff0c;如果你也有这种困扰&#xff0c;那么&#xff0c;不妨看看这篇文章&#xff01; 首先&#xff0c;对于报班VS自学这个问题&#xff0c;小编的建议是&#xff1a;…

MES管理系统的实施难点以及解决方案

随着智能制造的浪潮席卷全球&#xff0c;MES管理系统成为了众多制造企业提升竞争力的关键武器。MES管理系统以其强大的功能&#xff0c;能够有效连接企业的上层ERP系统与底层自动化设备&#xff0c;实现生产过程的实时监控与优化。然而&#xff0c;实施MES管理系统并非一帆风顺…

TIMESTAMP 和DATETIME 的区别

DATETIME类型 存储范围&#xff1a;DATETIME类型可以存储从1000-01-01 00:00:00到9999-12-31 23:59:59的日期和时间值&#xff0c;精度可以达到微秒级别。时区处理&#xff1a;DATETIME类型不与特定时区关联。它直接存储你提供的日期和时间值&#xff0c;不包含任何时区信息。…

SSM超市管理系统-计算机毕业设计源码10428

目 录 摘要 1 绪论 1.1 研究意义 1.2国内外研究现状 1.3论文结构与章节安排 2 超市管理系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 …

Arduino平台软硬件原理及使用——SR04超声波传感器的使用

文章目录&#xff1a; 一、超声波传感器工作原理 二、SR04超声波库的使用 三、SR04超声波传感器在Arduino中的使用 一、超声波传感器工作原理 如上图所示&#xff1a;HCSR04超声波传感器拥有4个针脚&#xff0c;除了VCC接正极、GND接负极外&#xff0c;还有两个引脚“Trig”及“…

vue-主题切换

themeName/index.vue页面: <template><div class"theme-view"><div click"themeClick" class"theme-btn">切换颜色</div><br>{{themeName white ? 白色 : 深色}}主题页面</div> </template><sc…

pytorch统计学分布

1、pytorch统计学函数 import torcha torch.rand(2,2) print(a) print(torch.sum(a, dim0)) print(torch.mean(a, dim0)) print(torch.prod(a, dim0))print(torch.argmax(a, dim0)) print(torch.argmin(a, dim0)) print(torch.std(a)) print(torch.var(a)) print(torch.median…

UE4 Unlua的快速使用

目录 Unlua的使用前言下载Unlua插件插件安装快速入门语法汇总模块导入多行字符串官方静态方法调用蓝图方法调用重载蓝图中的方法主动调用被重载的蓝图方法输入绑定动态绑定Lua脚本委托容器使用 延迟与协程的使用C 调用Lua 静态导出自定义类型到Lua使用网络UMG资源释放自定义加载…

秋招季的策略与行动指南:提前布局,高效备战,精准出击

6月即将进入尾声&#xff0c;一年一度的秋季招聘季正在热火进行中。对于即将毕业的学生和寻求职业发展的职场人士来说&#xff0c;秋招是一个不容错过的黄金时期。 秋招的序幕通常在6月至9月间拉开&#xff0c;名企们纷纷开启网申的大门。在此期间&#xff0c;求职备战是一个系…