Geek漫游指南

How-to-work-by-tomcat

date
slug
how-to-work-by-tomcat
author
status
Public
tags
技术分享
summary
type
Post
thumbnail
category
💻 Backend
updatedAt
Oct 29, 2023 07:21 AM

分析Tomcat源码的第一步

首先让我们弄懂Tomcat架构体系

notion image

The Server:

Tomcat它自身,属于Web Server的一个实例(instance)。属于最上层的组件。可以通过端口打开/关闭,可以切换成Debug模式。在JVM中,只能创建一个Server Instance。通过端口的设置用以达到在一台机子运行不同端口的Server,每台Server相互独立,有自己的应用配置,应用之间不会造成相互的影响。

Service:

Service是Engine容器的上层组件,封装了多个接受请求的Connector(连接器)和一个Engine容器。在Catalina(Servlet )容器里中,它通过Http处理Servlet的请求并决定将请求传递到虚拟主机还是上下文(Context)。
每个Service代表一组Connector和一个Container(容器),用以管理客户端和服务器之间的连接。这些容器接受连接器的请求并处理这些请求。容器包含Web应用程序。 它负责接受请求,将请求路由到指定的Web应用程序和特定资源,并返回请求处理的结果。 连接器位于发出请求的客户端和容器之间,可以提供一些服务,例如SSL支持。

Connectors :

连接器可以链接应用程序与客户端,处理请求和返回对象。默认的端口是8080,如果不指定默认端口则为80。

Engines:

虚拟主机引擎,一个Tomcat Server只有一个引擎。引擎将连接器的所有请求交给相应的(Host)虚拟主机去处理。在多宿主机器中。 引擎可能包含代表一组Web应用程序的主机和代表单个Web应用程序的上下文。
  • Host - 虚拟主机,通过其IP地址或主机名来区分
  • Contexts - Web Application

Realms:

管理引擎的User Authentication And Authorization(用户身份验证和授权), 通过应用配置,允许管理员对一个资源或一组资源设置权限。

Valves:

A Valve element represents a component that will be inserted into the request processing pipeline for the associated Catalina container (Engine, Host, or Context). Individual Valves have distinct processing capabilities

每个层级的容器(Engine, Host、Context, Wrapper )均有对应的基础Valve实现,同时维护了一个Pipeline实例,我们可以在任何层级的容器 上针对请求处理进行扩展https://zhuanlan.zhihu.com/p/127824362
  • Access Log org.apache.catalina.valves.AccessLogValve 它创建日志文件以跟踪客户端访问信息。
  • Remote Address Filter org.apache.catalina.filters.RemoteAddrFilter 允许将提交请求的客户机的IP地址与一个或多个正则表达式进行比较,并允许该请求继续或拒绝处理来自此客户机的请求。
  • Remote Host Filter org.apache.catalina.filters.RemoteHostFilter 允许将提交此请求的客户机的主机名与一个或多个正则表达式进行比较,并允许请求继续或拒绝处理来自此客户机的请求
  • Request Dumper org.apache.catalina.filters.RequestDumperFilter 记录来自请求和响应对象的信息
    • 更多详细的可以在官方文档找到
      <http://tomcat.apache.org/tomcat-9.0-doc/config/filter.html

Loggers:

在Tomcat中,使用的是Apcache Common的Juli日志框架。以确保Tomcat的内部日志和Web Application的日志隔离开来。

JVM中的类加载机制

notion image
jvmclassloader
Java通过类加载器动态的将Class加载到虚拟机中,而默认的类加载机制是通过双亲委派的方式来实现。
Class并不会一次性全都加载到内存里面,只是当被用到了才会被加载。
加载过程: 加载->链接->初始化 (Loading, Linking, and Initializing)。
通过加载类或者接口的二进制文件,链接并组合类和接口到虚拟机以便执行。初始化是执行类和接口的的初始化方法
我们可以使用这个命令,来查看一大堆被加载器加载的类

verbose:class

notion image
1590132478877
notion image
after_verbose
由上图我们知道,JVM内置了三个类加载器
  1. Boostrap ClassLoader (jre.jar or specify parameters)
      • Xbootclasspath 参数指定
  1. Extension ClassLoader (jre*.jar or specify parameters)
    1. java.ext.dirs 系统变量所指定的路径下的jar
  1. Application ClassLoader (classpath)
类分别被不同的加载器加载,通过 getClassLoader()我们可以知道加载类的加载器是哪一个。
notion image
loadClass
通过自底向上查看类是否被加载过,类加载器在通过名称加载类,如果父类加载器找不到,就会从子类加载器开始找,一直找不到就会抛出一个NoClassDefFoundError或ClassNotFoundException。
让我们再看Tomcat的类加载器
Bootstrap | System | Common / \ Webapp1 Webapp2 ...
  • Bootstrap
    • 该类加载器包含Java虚拟机提供的基本运行时类,以及系统扩展目录($ JAVA_HOME / jre / lib / ext)中存在的JAR文件中的所有类。 注意:某些JVM可能将其实现为多个类加载器,或者可能根本不可见(作为类加载器)
  • System
    • 这个类装入器通常是从类路径环境变量的内容初始化的。所有这些类对Tomcat内部类和web应用程序都是可见的。但是,标准的Tomcat启动脚本($CATALINA_HOME/bin/catalina.sh或%CATALINA_HOME%.bat)完全忽略了类路径环境变量本身的内容,而是从以下库构建系统类加载器:
      $CATALINA_HOME/bin/bootstrap.jar -包含用于初始化Tomcat服务器的main()方法,以及它所依赖的类加载器实现类
      $ CATALINA_BASE / bin / tomcat-juli.jar或$ CATALINA_HOME / bin / tomcat-juli.jar —记录实现类。 其中包括对java.util.logging API的增强类,称为Tomcat JULI
      $ CATALINA_HOME / bin / commons-daemon.jar — Apache Commons Daemon项目中的类。 这
  • Common
    • 该类加载器包含让Tomcat内部类和所有Web应用程序都可用的类
      通常,不应将应用程序类放在这里。 此类加载器搜索的位置由$ CATALINA_BASE / conf / catalina.properties中的common.loader属性定义。 默认设置将按列出的顺序搜索以下位置:
      $ CATALINA_BASE / lib中解压缩的类和资源
      $ CATALINA_BASE / lib中的JAR文件
      $ CATALINA_HOME / lib中解压缩的类和资源
      $ CATALINA_HOME / lib中的JAR文件
      默认情况,会加载tomcat/bin的所有jar包。
  • WebappX
    • 为在单个Tomcat实例中部署的每个Web应用程序创建的一个类加载器。 Web应用程序的/ WEB-INF / classes目录中的所有解压缩的类和资源,以及Web应用程序的/ WEB-INF / lib目录下的JAR文件中的类和资源,这些类和资源对应用程序是可访问的,但是外部是不能访问的。

      也就是说不同的Web应用程序是不能互相访问的

除了上述加载器,Tomcat还有两个自定义加载器
Catalina 负责加载Tomcat内部的类 Shared 负责加载Tomcat下所有Web应用程序复用类
notion image
20200522172251
回到Bootstrap.init()方法
notion image
1590141122454
使用Catalina加载器初始化Catalina类
notion image
MZ}3A%CML[W36NFVK]68TGJ
这边是加载命令行的命令
重要的是在启动时候的load()方法
notion image
1590505468569

通过保存一个Object的引用,再用反射去调用了Catalina.load(),实现了代码的解耦。

Servier.xml的加载与解析

notion image
ConfigFileLoader
notion image
config_xml
server.xml是放置Tomcat一些参数的地方。当找不到会去加载server-embed.xml

在加载完TomcatBaseConfig之后,也会加载一个组件[Digester]

Digester是解析server.xml参数的地方。
在Java中,解析XML的方式有两种:一种是将文件读取到内存中,构造一个DOM树(对象模型集合),也就是我们俗称的DOM解析,最常用的就是DOM4J。另外一种SAX解析,一行一行的读取XML文件,通过监听获取节点。在读取完毕后释放内存,只会在某一时刻存在一行的内存数据。
有兴趣可以参考这篇文章 https://www.cnblogs.com/jiaan-geng/p/4866009.html

Digester主要是对SAX的封装和抽象

让我们看下面这一组参数:
<Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /></>
Catalina.createStartDigester()中
// Configure the actions we will be using digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer","org.apache.catalina.Server");
获取Server前缀的一组参数,读取到StandardServer类中,其中有一个私有变量port,默认值是8005,还有一个私有变量address,初始化值是“localhost”,这也就是为什么tomcat默认的host是本机了。

初始化生命周期组件 LifeCycle

最上层的接口定义,以及生命周期的常量
public interface Lifecycle { // ----------------------------------------------------- Manifest Constants /** * The LifecycleEvent type for the "component before init" event. */ public static final String BEFORE_INIT_EVENT = "before_init"; /** * The LifecycleEvent type for the "component after init" event. */ public static final String AFTER_INIT_EVENT = "after_init"; /** * The LifecycleEvent type for the "component start" event. */ public static final String START_EVENT = "start"; /** * The LifecycleEvent type for the "component before start" event. */ public static final String BEFORE_START_EVENT = "before_start"; /** * The LifecycleEvent type for the "component after start" event. */ public static final String AFTER_START_EVENT = "after_start"; /** * The LifecycleEvent type for the "component stop" event. */ public static final String STOP_EVENT = "stop"; /** * The LifecycleEvent type for the "component before stop" event. */ public static final String BEFORE_STOP_EVENT = "before_stop"; /** * The LifecycleEvent type for the "component after stop" event. */ public static final String AFTER_STOP_EVENT = "after_stop"; /** * The LifecycleEvent type for the "component after destroy" event. */ public static final String AFTER_DESTROY_EVENT = "after_destroy"; /** * The LifecycleEvent type for the "component before destroy" event. */ public static final String BEFORE_DESTROY_EVENT = "before_destroy"; /** * The LifecycleEvent type for the "periodic" event. */ public static final String PERIODIC_EVENT = "periodic"; /** * The LifecycleEvent type for the "configure_start" event. Used by those * components that use a separate component to perform configuration and * need to signal when configuration should be performed - usually after * {@link #BEFORE_START_EVENT} and before {@link #START_EVENT}. */ public static final String CONFIGURE_START_EVENT = "configure_start"; /** * The LifecycleEvent type for the "configure_stop" event. Used by those * components that use a separate component to perform configuration and * need to signal when de-configuration should be performed - usually after * {@link #STOP_EVENT} and before {@link #AFTER_STOP_EVENT}. */ public static final String CONFIGURE_STOP_EVENT = "configure_stop"; // --------------------------------------------------------- Public Methods /** * 添加监听器 * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener); /** * 获取所有的监听器 * * @return An array containing the life cycle listeners associated with this * life cycle. If this component has no listeners registered, a * zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners(); /** * 移除当前的监听器 * * @param listener The listener to remove */ public void removeLifecycleListener(LifecycleListener listener); /** * 初始化方法,在INIT_EVENT成功之后执行 * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void init() throws LifecycleException; /** * 启动方法,在调用public之外的方法之前。执行顺序BEFORE_START_EVENT、START_EVENT、 * AFTER_START_EVENT */ public void start() throws LifecycleException; /** * 在STOP_EVENT时触发,优雅的关闭。 */ public void stop() throws LifecycleException; /** * 在完成DESTROY_EVENT状态时,调用销毁方法 */ public void destroy() throws LifecycleException; /** * 获取当前组件的生命周期状态 */ public LifecycleState getState(); /** * 获取String类型的生命周期状态 */ public String getStateName(); /** * Marker interface used to indicate that the instance should only be used * once. Calling {@link #stop()} on an instance that supports this interface * will automatically call {@link #destroy()} after {@link #stop()} * completes. */ public interface SingleUse { }}

组件的生命周期

public enum LifecycleState { NEW(false, null), //新状态 INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT), //正在初始化 INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT), //初始化完毕 STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT), //开始启动 STARTING(true, Lifecycle.START_EVENT), //启动中 STARTED(true, Lifecycle.AFTER_START_EVENT), //启动完毕 STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT), //准备停止 STOPPING(false, Lifecycle.STOP_EVENT), //停止中 STOPPED(false, Lifecycle.AFTER_STOP_EVENT), //已停止 DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT), //正在释放or正在销毁 DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT), //已消亡 FAILED(false, null); //失败 private final boolean available; private final String lifecycleEvent; private LifecycleState(boolean available, String lifecycleEvent) { this.available = available; this.lifecycleEvent = lifecycleEvent; } /** * May the public methods other than property getters/setters and lifecycle * methods be called for a component in this state? It returns * <code>true</code> for any component in any of the following states: * <ul> * <li>{@link #STARTING}</li> * <li>{@link #STARTED}</li> * <li>{@link #STOPPING_PREP}</li> * </ul> * * @return <code>true</code> if the component is available for use, * otherwise <code>false</code> */ public boolean isAvailable() { return available; } public String getLifecycleEvent() { return lifecycleEvent; }}
/** * General event for notifying listeners of significant changes on a component * that implements the Lifecycle interface. * * @author Craig R. McClanahan */public final class LifecycleEvent extends EventObject { private static final long serialVersionUID = 1L; /** * Construct a new LifecycleEvent with the specified parameters. * * @param lifecycle Component on which this event occurred * @param type Event type (required) * @param data Event data (if any) */ public LifecycleEvent(Lifecycle lifecycle, String type, Object data) { super(lifecycle); this.type = type; this.data = data; } /** * The event data associated with this event. */ private final Object data; /** * The event type this instance represents. */ private final String type; /** * @return the event data of this event. */ public Object getData() { return data; } /** * @return the Lifecycle on which this event occurred. */ public Lifecycle getLifecycle() { return (Lifecycle) getSource(); } /** * @return the event type of this event. */ public String getType() { return this.type; }}

LifecycleBase

public abstract class LifecycleBase implements Lifecycle {}

Base implementation of the Lifecycle interface that implements the

state transition rules for start() and stop()

生命周期接口的基本实现,它实现了start()和stop()的状态转换规则
Tomcat中的组件都需要实现Lifecycle接口,作用于对组件的启动、初始化、关闭的标准流程化
@Override public final synchronized void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } try { setStateInternal(LifecycleState.INITIALIZING, null, false); initInternal(); //各个子类实现protected该方法初始化组件 setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { handleSubClassException(t, "lifecycleBase.initFail", toString()); } }
实现了该接口的组件,当状态改变时会触发该事件监听器
public interface LifecycleListener { /** * Acknowledge the occurrence of the specified event. * * @param event LifecycleEvent that has occurred */ public void lifecycleEvent(LifecycleEvent event); }
Tomcat各个组件都是先分别执行init()方法,初始化完毕之后才依次执行start()方法
关于JNDI
https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
ContextConfig负责加载上下文配置,比如 web.xml 由webConfig()加载
在Tomcat中
第一个初始化的组件是Global JNDI resources
/** * Helper class used to initialize and populate the JNDI context associated * with each context and server. * * @author Remy Maucherat */ public class NamingContextListener implements LifecycleListener, ContainerListener, PropertyChangeListener { }
源码注释中写道,这是一个用来初始化和填充每个上下文和服务器关联的JNDI上下文

Tomcat Request对象的生成

一个Request的请求大概会经过以下步骤
线程->Socket->Protocol->HTTP11>CoyoteAdapter->StandardEngineValue管道->AccessLogValue管道
  • > ErrorReportValue->StandardHostValue->AuthenticatorBase->StandardContextValue->StandardWrapperValue->ApplicationFilterChain->ErrorPageFilter->最终到达HttpServlet

连接器与容器的桥梁——CoyoteAdapter

未完………..