当前位置: 首页 > news >正文

Tomcat - 从源码阅读中分析核心组件

前言

在上篇文章 Tomcat - 源码阅读环境搭建 中,我们搭建了 Tomcat 源码阅读的环境,并且我们知道了启动类是 org.apache.catalina.startup.Bootstrap,接下来我们就来分析一下 Tomcat 的核心组件都有哪些,以及它们之间的依赖关系

核心组件

Tomcat 的默认配置文件是 conf/server.xml

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

	// 处理请求
  <Service name="Catalina">
  	<-- 配置监听端口 -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

	// 真正处理请求的是 Engine
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

从配置文件中,我们知道 Tomcat 都包含了哪些组件,以及各个组件之间的依赖关系:

在这里插入图片描述

下面是 Tomcat 的架构图(这个图我也忘记是从哪个网站保存的了,侵删),包含了 Tomcat 的核心组件,以及它们之间的依赖关系:

请添加图片描述

接下来我们从代码中来看一下关于上面这些组件的定义及其依赖关系

代码分析

Server

  • 首先我们来看一下 Server 的代码实现:
public interface Server extends Lifecycle {

	public NamingResourcesImpl getGlobalNamingResources();
	
	// server 中包含 GlobalNamingResources
    public void setGlobalNamingResources(NamingResourcesImpl globalNamingResources);

	// server 中包含 Service,默认是有一个 Catalina Service
	public void addService(Service service);

	// Service 可以有很多,是一个 Set 集合
	public Service[] findServices();
	...
}

Service

  • 接着查看 Service 的实现,其中会包含 EngineConnectorExecutor
public interface Service extends Lifecycle {

	// Service 中可以有一个 Engine
	public Engine getContainer();

    public void setContainer(Engine engine);

	public void addConnector(Connector connector);

	// Service 中有多个 Connector,监听不同的端口
    public Connector[] findConnectors();

	public void addExecutor(Executor ex);

	// Service 中有多个 Executor
    public Executor[] findExecutors();
    
	...
}

Connector

Connector 是接收请求的,请求进来一定是 Http 协议,所以 Connector 中包含了协议处理器 ProtocolHandler

public class Connector extends LifecycleMBeanBase  {}

Engine

Engine 中可以有很多的 Host

public interface Engine extends Container {

	public String getDefaultHost();

	// 设置默认的 Host
    public void setDefaultHost(String defaultHost);
	...
}
Host

Host 代表虚拟 DNS,根据 Host 可以进行虚拟主机隔离

public interface Host extends Container {}

查看 Host 的实现类 `StandardHost:

public class StandardHost extends ContainerBase implements Host {
	...
	@Override
    public void addChild(Container child) {

		// 如果不是 Context 类型的直接抛出异常
        if (!(child instanceof Context)) {
            throw new IllegalArgumentException
                (sm.getString("standardHost.notContext"));
        }

        child.addLifecycleListener(new MemoryLeakTrackingListener());

        // Avoid NPE for case where Context is defined in server.xml with only a
        // docBase
        Context context = (Context) child;
        if (context.getPath() == null) {
            ContextName cn = new ContextName(context.getDocBase(), true);
            context.setPath(cn.getPath());
        }

        super.addChild(child);

    }
}
Context
public interface Context extends Container, ContextBind {}

一个 Web 应用就是一个 ContextContext 也有一个标准实现类 StandardContext

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {
    ...
	@Override
    public void addChild(Container child) {

        // Global JspServlet
        Wrapper oldJspServlet = null;

		// 如果不是 Wrapper,直接抛出异常
        if (!(child instanceof Wrapper)) {
            throw new IllegalArgumentException
                (sm.getString("standardContext.notWrapper"));
        }

        boolean isJspServlet = "jsp".equals(child.getName());

        // Allow webapp to override JspServlet inherited from global web.xml.
        if (isJspServlet) {
            oldJspServlet = (Wrapper) findChild("jsp");
            if (oldJspServlet != null) {
                removeChild(oldJspServlet);
            }
        }

        super.addChild(child);

        if (isJspServlet && oldJspServlet != null) {
            /*
             * The webapp-specific JspServlet inherits all the mappings
             * specified in the global web.xml, and may add additional ones.
             */
            String[] jspMappings = oldJspServlet.findMappings();
            for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
                addServletMappingDecoded(jspMappings[i], child.getName());
            }
        }
    }
	...
}

Context 中添加的子容器是 Wrapper

Wrapper
public interface Wrapper extends Container {}

Wrapper 中不能再添加子组件了,默认实现类是 StandardWrapper,主要是跟 Servlet 相关的一些操作,每一个 Servlet 会封装为一个 Wrapper

在这里插入图片描述

生命周期

通过以上代码分析,我们对 Tomcat 的组件有了一个大致的了解,其中我们可以看到一个共同点,就是它们都继承了 Lifecycle 这一个接口:

在这里插入图片描述

Lifecycle 定义了几个生命周期相关的方法,比如:initstartstopdestroy 等,每一个实现这个接口的组件都具有这几个生命周期。

接着我们看一下 Ligecycle 的子接口 Container,同时也被 EngineHost 等组件所实现

public interface Container extends Lifecycle {
	...

	public void addChild(Container child);

	public Pipeline getPipeline();
	...
}

Pipeline 中又可以添加很多的 valvePipeline 用于数据流转,Valve 用于数据处理:

public interface Pipeline extends Contained {
	...
	public void addValve(Valve valve);

	public Valve[] getValves();
	...
}

比如上面 serve.xmlHost 节点默认的 Valveorg.apache.catalina.valves.AccessLogValve,用于记录访问日志。

LifecycleBaseContainerBase 利用模板模式,定义好核心步骤,具体逻辑留给组件自己实现。

总结

以上,我们对 Tomcat 的各个组件及其之间的依赖关系有了一个大致的了解,接下来我们就来看一下 Tomcat 的初始化流程、启动流程、请求处理流程。

相关文章:

  • Android Glide应用中遇到问题
  • 个人云服务的搭建(折腾)之旅
  • 浅谈js中的深拷贝和浅拷贝
  • Hbase-3-4-Hbase读写数据流程
  • Oracle——常用的几种函数(含案例)
  • 【Linux】多线程 —— 线程概念 | 线程控制
  • windows10创建ssh git gitee使用公钥私钥【自留收藏】
  • 微信搜题接口API功能
  • YBTOJ 树状数组 二进制
  • 无胁科技-TVD每日漏洞情报-2022-8-30
  • Java8 特性(一):函数、Lambok、Stream
  • 定时器(Quartz)
  • 神经网络实现线性回归,神经网络是回归算法吗
  • MFC调用VLC库播放中文路径导致崩溃的问题
  • 微信公众号搜题功能接口
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Angular6错误 Service: No provider for Renderer2
  • CSS相对定位
  • Intervention/image 图片处理扩展包的安装和使用
  • Java面向对象及其三大特征
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • LeetCode29.两数相除 JavaScript
  • MySQL QA
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • Web标准制定过程
  • 反思总结然后整装待发
  • 计算机常识 - 收藏集 - 掘金
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 前端_面试
  • 微信小程序--------语音识别(前端自己也能玩)
  • 云大使推广中的常见热门问题
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (function(){})()的分步解析
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (学习日记)2024.01.09
  • (译)计算距离、方位和更多经纬度之间的点
  • (转)编辑寄语:因为爱心,所以美丽
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • 、写入Shellcode到注册表上线
  • ./configure、make、make install 命令
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET 发展历程
  • .net 受管制代码
  • .net反编译的九款神器
  • .NET开发者必备的11款免费工具
  • .NET中 MVC 工厂模式浅析
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @GetMapping和@RequestMapping的区别