tomcat 分析
参考: https://u19900101.github.io/2021-09-07-tomcat/
整体架构
Tomcat 的两个核心功能:
1) 处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。
2) 加载和管理 Servlet,以及具体处理 Request 请求。
因此 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

Tomcat 为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作 Service 组件。这里请你注意,Service 本身没有做什么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。Tomcat 内可能有多个 Service,这样的设计也是出于灵活性的考虑。通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
连接器和容器

连接器
EndPoint
1) EndPoint : Coyote 通信端点,即通信监听的接口,是具体 Socket 接收和发送处理器,是对传输层的抽象,因此 EndPoint 用来实现 TCP/IP 协议的。
2) Tomcat 并没有 EndPoint 接口,而是提供了一个抽象类 AbstractEndpoint ,里面定义了两个内部类:Acceptor 和 SocketProcessor。Acceptor 用于监听 Socket 连接请求。 SocketProcessor 用于处理接收到的 Socket 请求,它实现 Runnable 接口,在 Run 方法里调用协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor 被提交到线程池来执行。而这个线程池叫作执行器(Executor),我在后面的专栏会详细介绍 Tomcat 如何扩展原生的 Java 线程池。
Processor: Coyote 协议处理接口,如果说 EndPoint 是用来实现 TCP/IP 协议的,那么 Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理, Processor 是对应用层协议的抽象。
ProtocolHandler: Coyote 协议接口,通过 Endpoint 和 Processor ,实现针对具体协议的处理能力。Tomcat 按照协议和 I/O 提供了 6 个实现类 : AjpNioProtocol , AjpAprProtocol, AjpNio 2 Protocol , Http 11 NioProtocol ,Http 11 Nio 2 Protocol , Http 11 AprProtocol。我们在配置 tomcat/conf/server. xml 时,至少要指定具体的 ProtocolHandler , 当然也可以指定协议名称,如 : HTTP/1.1 ,如果安装了 APR,那么将使用 Http 11 AprProtocol ,否则使用 Http 11 NioProtocol 。
Adapter:由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat 定义了自己的 Request 类来“存放”这些请求信息。ProtocolHandler接口负责解析请求并生成 Tomcat Request 类。但是这个 Request 对象不是标准的 ServletRequest,也就意味着,不能用 Tomcat Request 作为参数来调用容器。Tomcat 设计者的解决方案是引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 Sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 Service 方法。
容器
Engine:表示整个 Catalina 的 Servlet 引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine,但是一个引擎可包含多个 Host
Host:代表一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可包含多个 Context
Context:表示一个 Web 应用程序,一个 Web 应用可包含多个 Wrapper
Wrapper:表示一个 Servlet,Wrapper 作为容器中的最底层,不能包含子容器
tomcat 的一些问题
关于Tomcat中的三个Context的理解
参考: https://yzddmr6.com/posts/tomcat-context/
idea 部署并运行 tomcat 源码
参考: https://www.cnblogs.com/grasp/p/10061577.html ,照这个弄有问题。
使用 maven(3.3.9) 部署,tomcat(8.5.88),java(11.0.1)
下载源码后,解压,在源码目录文件夹下创建 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>Tomcat8.5</artifactId>
<name>Tomcat8.5</name>
<version>8.5</version>
<build>
<finalName>Tomcat8.5</finalName>
<sourceDirectory>java</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>test</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.3</version>
</dependency>
<dependency>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.unboundid/unboundid-ldapsdk -->
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>4.0.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
idea 导入这个项目(maven),找到 java/org/apache/catalina/startup/Bootstrap.java ,启动。有报错,一个一个解决。
在 tomcat 的源码 org.apache.catalina.startup.ContextConfig 中的 configureStart 函数中手动将 JSP 解析器初始化:在 webConfig(); 后面添加下面这句
context.addServletContainerInitializer(new JasperInitializer(), null);
报错解决
问题:
找不到:符号: 变量 CookieFilter 位置: 类 util. TestCookieFilter
解决:在tomcat/test/utiltest/util包中新建CookieFilter类
package util;
import java.util.Locale;
import java.util.StringTokenizer;
/**
* Cookie过滤
*
* @author Chova
* @date 2020-09-04
*/
public class CookieFilter {
private static final String OBFUSCATED = "[obfuscated]";
private CookieFilter() {
// Hide default constructor
}
public static String filter(String cookieHeader, String sessionId) {
StringBuilder sb = new StringBuilder(cookieHeader.length());
// Cookie name value pairs are ';' separated.
// Session IDs don't use ; in the value so don't worry about quoted
// values that contain ;
StringTokenizer st = new StringTokenizer(cookieHeader, ";");
boolean first = true;
while (st.hasMoreTokens()) {
if (first) {
first = false;
} else {
sb.append(';');
}
sb.append(filterNameValuePair(st.nextToken(), sessionId));
}
return sb.toString();
}
private static String filterNameValuePair(String input, String sessionId) {
int i = input.indexOf('=');
if (i == -1) {
return input;
}
String name = input.substring(0, i);
String value = input.substring(i + 1, input.length());
return name + "=" + filter(name, value, sessionId);
}
public static String filter(String cookieName, String cookieValue, String sessionId) {
if (cookieName.toLowerCase(Locale.ENGLISH).contains("jsessionid") &&
(sessionId == null || !cookieValue.contains(sessionId))) {
cookieValue = OBFUSCATED;
}
return cookieValue;
}
}
参考: https://blog.csdn.net/JewaveOxford/article/details/108431280