Tomcat卷一 ----架构和初始化源码分析
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat卷一 ----架构和初始化源码分析相关的知识,希望对你有一定的参考价值。
Tomcat卷一
- Tomcat 基础概念扫盲
- Tomcat 架构
- Tomcat 启动流程
- 流程
- 源码解析
- Lifecycle
- 各组件的默认实现
- init源码流程追踪
- start源码追踪
- 1. BootStrap.start() ---> Catalina.start()
- 2. Server.start() ---> StandardServer.startInternal()
- 3. Service.start() ---> StandardService.startInternal()
- 4. Engine.start() --->StandardEngine.startInternal()
- 5. Host.start() ---> StandardHost.startInternal()
- 6. StandardContext.startInternal()
- 7. StandardWrapper.startInternal()
- 8. Connector.startInternal()
- 9.ProtocolHandler.start() ---> AbstractProtocol.start()
- 10.Endpoint.start() ---> NioEndpoint.startInternal()
- 总结
本系列主要在于梳理tomcat的整体架构和源码剖析,只挑重点流程进行分析
Tomcat 基础概念扫盲
web 概念
1). 软件架构
1. C/S: 客户端/服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> QQ , 360 ....
2. B/S: 浏览器/服务器端 ‐‐‐‐‐‐‐‐‐‐‐‐> 京东, 网易 , 淘宝
2). 资源分类
1. 静态资源: 所有用户访问后,得到的结果都是一样的,称为静态资源。静态资
源可以直接被浏览器解析。
* 如: html,css,javascript,jpg
2. 动态资源: 每个用户访问相同资源后,得到的结果可能不一样 , 称为动态资
源。动态资源被访问后,需要先转换为静态资源,再返回给浏览器,通过浏览器进行解析。
* 如:servlet/jsp,php,asp....
3). 网络通信三要素
1. IP:电子设备(计算机)在网络中的唯一标识。
2. 端口:应用程序在计算机中的唯一标识。 0~65536
3. 传输协议:规定了数据传输的规则
1. 基础协议:
1. tcp : 安全协议,三次握手。 速度稍慢
2. udp:不安全协议。 速度快
常见的web服务器
概念
1). 服务器:安装了服务器软件的计算机
2). 服务器软件:接收用户的请求,处理请求,做出响应
3). web服务器软件:接收用户的请求,处理请求,做出响应。
在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
常见web服务器软件
1). webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
2). webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
3). JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
4). Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范 servlet/jsp。开源的,免费的。
Tomcat 历史
1) Tomcat 最初由Sun公司的软件架构师 James Duncan Davidson 开发,名称为 “JavaWebServer”。
2) 1999年 ,在 Davidson 的帮助下,该项目于1999年于apache 软件基金会旗下的 JServ 项目合并,并发布第一个版本(3.x), 即是现在的Tomcat,该版本实现了 Servlet2.2 和 JSP 1.1 规范 。
3) 2001年,Tomcat 发布了4.0版本, 作为里程碑式的版本,Tomcat 完全重新设计了 其架构,并实现了 Servlet 2.3 和 JSP1.2规范。
Tomcat 安装
Tomcat官网下载源码包即可,这里使用的是tomcat 8.5.42版本
Tomcat 目录结构
Tomcat 启动停止
启动
双击 bin/startup.bat 文件
停止
双击 bin/shutdown.bat 文件
访问
http://localhost:8080
Tomcat源码
运行
1) 解压zip压缩包
2) 进入解压目录,并创建一个目录,命名为home , 并将conf、webapps目录移入 home 目录中
3) 在当前目录下创建一个 pom.xml 文件,引入tomcat的依赖包
<?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>3.4</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>
</dependencies>
</project>
4) 在idea中, 导入该工程
5) 配置idea的启动类, 配置 MainClass , 并配置 VM 参数
由于移动了源码文件的位置,因此需要手动指定新文件的位置,这里用绝对路径,改成自己的绝对路径防止文件找不到
-Dcatalina.home=C:/Users/zdh/Desktop/tomcat/tomcatSrcCode/apache-tomcat-8.5.42-src/home
-Dcatalina.base=C:/Users/zdh/Desktop/tomcat/tomcatSrcCode/apache-tomcat-8.5.42-src/home
-Djava.endorsed.dirs=C:/Users/zdh/Desktop/tomcat/tomcatSrcCode/apache-tomcat-8.5.42-src/home/endorsed
-Djava.io.tmpdir=C:/Users/zdh/Desktop/tomcat/tomcatSrcCode/apache-tomcat-8.5.42-src/home/temp
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=C:/Users/zdh/Desktop/tomcat/tomcatSrcCode/apache-tomcat-8.5.42-src/home/conf/logging.properties
6) 启动主方法, 运行Tomcat , 访问Tomcat 。
说明:如果编译build的时候出现Test测试代码报错,注释该代码即可。本文中的Tomcat源码util.TestCookieFilter类会报错,将其注释即可
运行项目,访问http://localhost:8080,得到结果:
原因是我们直接启动org.apache.catalina.startup.Bootstrap
的时候没有加载org.apache.jasper.servlet.JasperInitializer
,从而无法编译JSP
。
解决办法是在tomcat
的源码org.apache.catalina.startup.ContextConfig
中的configureStart
函数中手动将JSP
解析器初始化:
protected synchronized void configureStart()
// Called from StandardContext.start()
if (log.isDebugEnabled())
log.debug(sm.getString("contextConfig.start"));
if (log.isDebugEnabled())
log.debug(sm.getString("contextConfig.xmlSettings",
context.getName(),
Boolean.valueOf(context.getXmlValidation()),
Boolean.valueOf(context.getXmlNamespaceAware())));
webConfig();
//这里是需要添加的代码,其余是源码不需要动
context.addServletContainerInitializer(new JasperInitializer(), null);
if (!context.getIgnoreAnnotations())
applicationAnnotationsConfig();
if (ok)
validateSecurityRoles();
// Configure an authenticator if we need one
if (ok)
authenticatorConfig();
// Dump the contents of this pipeline if requested
if (log.isDebugEnabled())
log.debug("Pipeline Configuration:");
Pipeline pipeline = context.getPipeline();
Valve valves[] = null;
if (pipeline != null)
valves = pipeline.getValves();
if (valves != null)
for (int i = 0; i < valves.length; i++)
log.debug(" " + valves[i].getClass().getName());
log.debug("======================");
// Make our application available if no problems were encountered
if (ok)
context.setConfigured(true);
else
log.error(sm.getString("contextConfig.unavailable"));
context.setConfigured(false);
修改完后,项目再启动,我们再在浏览器访问http://localhost:8080/ ,就可以看到我们所熟悉的经典欢迎页面了
Tomcat 架构
Http工作原理
HTTP协议是浏览器与服务器之间的数据传送协议。
作为应用层协议,HTTP是基于TCP/IP 协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包 (Packet)传输,主要规定了客户端和服务器之间的通信格式。
从图上你可以看到,这个过程是:
1) 用户通过浏览器进行了一个操作,比如输入网址并回车,或者是点击链接,接着浏览 器获取了这个事件。
2) 浏览器向服务端发出TCP连接请求。
3) 服务程序接受浏览器的连接请求,并经过TCP三次握手建立连接。
4) 浏览器将请求数据打包成一个HTTP协议格式的数据包。
5) 浏览器将该数据包推入网络,数据包经过网络传输,最终达到端服务程序。
6) 服务端程序拿到这个数据包后,同样以HTTP协议格式解包,获取到客户端的意图。
7) 得知客户端意图后进行处理,比如提供静态文件或者调用服务端程序获得动态结果。
8) 服务器将响应结果(可能是HTML或者图片等)按照HTTP协议格式打包。
9) 服务器将响应数据包推入网络,数据包经过网络传输最终达到到浏览器。
10) 浏览器拿到数据包后,以HTTP协议的格式解包,然后解析数据,假设这里的数据是 HTML。
11) 浏览器将HTML文件展示在页面上。
那我们想要探究的Tomcat作为一个HTTP服务器,在这个过程中都做了些什么事情呢?
主 要是接受连接、解析请求数据、处理请求和发送响应这几个步骤。
Tomcat整体架构
Http服务器请求处理
浏览器发给服务端的是一个HTTP格式的请求,HTTP服务器收到这个请求后,需要调用服 务端程序来处理,所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同 的Java类来处理。
1) 图1 , 表示HTTP服务器直接调用具体业务类,它们是紧耦合的。
2) 图2,HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过 Servlet接口调用业务类。
因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与 业务类解耦的目的。
而Servlet接口和Servlet容器这一整套规范叫作Servlet规范。
Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功 能。
作为Java程序员,如果我们要实现新的业务功能,只需要实现一个Servlet,并把它 注册到Tomcat(Servlet容器)中,剩下的事情就由Tomcat帮我们处理了。
Servlet容器工作流程
为了解耦,HTTP服务器不直接调用Servlet,而是把请求交给Servlet容器来处理,那 Servlet容器又是怎么工作的呢?
当客户请求某个资源时,HTTP服务器会用一个ServletRequest对象把客户的请求信息封 装起来,然后调用Servlet容器的service方法,Servlet容器拿到请求后,根据请求的URL 和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创 建这个Servlet,并调用Servlet的init方法来完成初始化,接着调用Servlet的service方法 来处理请求,把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给 客户端。
Tomcat整体架构
我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了Tomcat要实现两个 核心功能:
1) 处理Socket连接,负责网络字节流与Request和Response对象的转化。
2) 加载和管理Servlet,以及具体处理Request请求。
因此Tomcat设计了两个核心组件连接器(Connector)
和容器(Container)
来分别做这 两件事情。连接器负责对外交流,容器负责内部处理。
tomcat核心组件分析
- Connector处理 http协议+端口号
- Host对应域名
- Context是tomcat应用执行上下文,默认是/,这里设置为lunban
- Servlet对应资源: 这里分为动态资源和静态资源,静态资源例如后缀是.html,.jpg的直接返回即可,动态资源还需要交给Servlet进行处理后才能返回处理后的静态资源
tomcat核心组件协作过程
科普时间到:
- 端口号的作用
端口号可以用来标识同一个主机上通信的不同应用程序,端口号+IP地址就可以组成一个套接字,用来标识一个进程
- 一个进程是否可以bind多个端口号?
可以, 因为一个进程可以打开多个文件描述符,而每个文件描述符都对应一个端口号,所以一个进程可以绑定多个端口号
- 一个端口号是否可以被多个进程绑定?
不可以, 如果进程先绑定一个端口号,然后在fork一个子进程,这样的话就可以是实现多个进程绑定一个端口号,但是两个不同的进程绑定同一个端口号是不可以的
这里tomcat服务器也可以设置多个端口号,因此对于的连接器也可以有多个
因为域名也可以配置多个,因此站点对应的也可以有多个
这里得出结论1: 连接器和站点是多对多的关系
结论2: 站点和应用上下文是一对多的关系,一个站点下面可以有多个应用上下文,这里应用上下文映射到物理层面对应一个文件夹,然后文件夹下面放着资源
结论3:应用上下文和资源也是一对多的关系,上面也说了,可以把应用上下文本质就是一个文件夹,而所谓的资源就是文件夹下面的.java文件动态资源文件或者.html,.jps等静态资源文件
应用上下文本质就是一堆资源文件集合存放的文件夹
演示
这是一个删减过后的server.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<!-- 连接器可以存在多个-->
<Connector port="8080" protocol="HTTP/1.1"/>
<Connector port="8081" protocol="HTTP/1.1"/>
<!-- engine可以看做是管理Host的一个容器,这里 defaultHost规定了默认的访问站点 -->
<Engine name="Catalina" defaultHost="localhost">
<!-- 站点也可以存在多个,这里对应Host标签里面的name -->
<!--这里默认的应用上下文是/,这里appBase是当前站点的根路径 -->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy以上是关于Tomcat卷一 ----架构和初始化源码分析的主要内容,如果未能解决你的问题,请参考以下文章