tomcat源码02 - 理解Tomcat架构设计
理解Tomcat架构设计
文章目录
- 理解Tomcat架构设计
- 一:准备知识
- 1:tomcat & catalina
- 2:什么是Servlet
- 二:不同角度看Tomcat架构
- 1:从组件的角度看
- 1.1:Server组件 - 服务器
- 1.2:Service组件 - 服务
- 1.3:container组件 - 容器
- 1.4:connector组件 - 连接器
- 2:从web.xml配置和模块对应角度
- 3:从一个完整请求角度来看
- 4:从源码的设计角度看
- 4.1:Jsper模块
- 4.2:Servlet和Jsp模块
- 4.3:Catalina模块
- 4.4:Connector模块
- 4.5:Resource模块
一:准备知识
1:tomcat & catalina
Tomcat的前身为Catalina,Catalina又是一个轻量级的Servlet容器
在美国,catalina是一个很美的小岛。所以Tomcat作者的寓意可能是想把Tomcat设计成一个优雅美丽且轻量级的web服务器
tomcat从4.x版本开始除了作为支持Servlet的容器外,额外加入了很多的功能,比如:jsp、el、naming等等
所以说Tomcat不仅仅是Catalina
所以:Tomcat是⼀个由⼀系列可配置(conf/server.xml)的组件构成的Web容器,⽽Catalina是Tomcat的servlet容器
从另⼀个⻆度来说,Tomcat 本质上就是⼀款 Servlet 容器
因为 Catalina 才是 Tomcat 的核⼼,其他模块都是为Catalina 提供⽀撑的。
2:什么是Servlet
所谓Servlet,其实就是Sun为了让Java能实现
动态可交互的网页
,从而进入Web编程领域而制定的一套标准![Servlet是web标准]
在互联网兴起之初,当时的Sun公司(后面被Oracle收购)已然看到了这次机遇,于是设计出了Applet来对Web应用的支持
不过事实却并不是预期那么得好,Sun悲催地发现Applet并没有给业界带来多大的影响
经过反思,Sun就想既然机遇出现了,市场前景也非常不错,总不能白白放弃了呀,怎么办呢?于是Servlet规范诞生了
一个Servlet主要做下面三件事情:
- 创建并填充Request对象,包括:URI、参数、method、请求头信息、请求体信息等
- 创建Response对象
- 执行业务逻辑,将结果通过Response的输出流输出到客户端
Servlet没有main方法,所以,如果要执行,则需要在一个容器里面才能执行,这个容器就是为了支持Servlet的功能而存在
Tomcat其实就是一个Servlet容器的实现
二:不同角度看Tomcat架构
目前找到的tomcat最全架构图
1:从组件的角度看
1.1:Server组件 - 服务器
它提供了一种优雅的方式来启动和停止整个系统,不必单独启停连接器和容器
它是Tomcat构成的顶级构成元素,所有一切均包含在Server中
1.2:Service组件 - 服务
Server可以运行多个服务。
比如一个Tomcat里面可运行订单服务、支付服务、用户服务等等;
Server的实现类StandardServer可以包含一个到多个Services, Service的实现类为StandardService调用了容器(Container)接口,其实是调用了Servlet Engine(引擎),而且StandardService类中也指明了该Service归属的Server;
Service内部还有很多支撑组件:
- Manager – 管理器,用于管理会话Session
- Logger – 日志器,用于管理日志
- Loader – 加载器,和类加载有关,只会开放给Context所使用
- Pipeline – 管道组件,配合Valve实现过滤器功能
- Valve – 阀门组件,配合Pipeline实现过滤器功能
- Realm – 认证授权组件
1.3:container组件 - 容器
可以看做Servlet容器;引擎(Engine)、主机(Host)、上下文(Context)和Wraper均继承自Container接口,所以它们都是容器。
1.4:connector组件 - 连接器
将Service服务和container容器进行连接
首先它需要注册到一个Service,它的作用就是把来自客户端的请求转发到Container(容器),它支持AJP, Http, Https协议
2:从web.xml配置和模块对应角度
上述模块的理解不是孤立的,它直接映射为Tomcat的web.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<Server port="8080" shutdown="SHUTDOWN"><!-- 配置Jasper监听器,用于支持JSP的编译 --><Listener className="org.apache.catalina.core.JasperListener" /><!-- 配置JRE内存泄露预防监听器,用于检测和预防Java应用中的内存泄露 --><Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /><!-- 配置线程本地泄露预防监听器,用于检测和预防Tomcat中可能出现的内存泄露问题 --><Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /><!-- 定义全局命名资源,这里配置了用户数据库资源 --><GlobalNamingResources><!-- 用户数据库资源,认证方式为Container,类型为org.apache.catalina.UserDatabase,描述为可更新和保存的用户数据库,工厂为MemoryUserDatabaseFactory,路径为conf/tomcat-users.xml --><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><!-- 配置Catalina服务 --><Service name="Catalina"><!-- 配置HTTP连接器,端口为8080,协议为HTTP/1.1,地址为127.0.0.1,连接超时时间为20000毫秒,SSL端口为8443 --><Connector port="8080" protocol="HTTP/1.1" address="127.0.0.1" connectionTimeout="20000" redirectPort="8443" /><!-- 配置引擎,名称为Catalina,默认主机为例.com --><Engine name="Catalina" defaultHost="example.com"><!-- 配置用户数据库领域的认证器,资源名称为UserDatabase --><Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/><!-- 配置主机,名称为例.com,应用基础目录为webapps,自动解压WAR文件,自动部署应用 --><Host name="example.com" appBase="webapps" unpackWARs="true" autoDeploy="true"><!-- 配置访问日志阀门,日志目录为logs,文件前缀为localhost_access_log.,文件后缀为.txt,日志模式为常见的Apache日志格式 --><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /></Host></Engine></Service>
</Server>
可以发现web.xml的Node结构和组件是对应的。
3:从一个完整请求角度来看
假设来自客户的请求为:http://localhost:8080/test/index.jsp 请求被发送到本机端口8080
被在那里侦听的Coyote HTTP/1.1 Connector发现,会发生如下步骤
- Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应
- Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host
- Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
- localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
- Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
- path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
- Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
- Context把执行完了之后的HttpServletResponse对象返回给Host
- Host把HttpServletResponse对象返回给Engine
- Engine把HttpServletResponse对象返回给Connector
- Connector把HttpServletResponse对象返回给客户browser
4:从源码的设计角度看
从功能的角度将Tomcat源代码分成5个子模块,分别是:
4.1:Jsper模块
这个子模块负责jsp页面的解析、jsp属性的验证,同时也负责将jsp页面动态转换为java代码并编译成class文件。
在Tomcat源代码中,凡是属于org.apache.jasper包及其子包中的源代码都属于这个子模块;
4.2:Servlet和Jsp模块
这个子模块的源代码属于javax.servlet包及其子包
如我们非常熟悉的javax.servlet.Servlet接口、javax.servet.http.HttpServlet类就位于这个子模块
4.3:Catalina模块
这个子模块包含了所有以org.apache.catalina开头的java源代码。
该子模块的任务是规范了Tomcat的总体架构,定义了Server、Service、Host、Connector、Context、Session及Cluster等关键组件及这些组件的实现
这个子模块大量运用了Composite设计模式。同时也规范了Catalina的启动及停止等事件的执行流程。
4.4:Connector模块
如果说上面三个子模块实现了Tomcat应用服务器的话,那么这个子模块就是Web服务器的实现。
所谓连接器(Connector)就是一个连接客户和应用服务器的桥梁
它接收用户的请求,并把用户请求包装成标准的Http请求(包含协议名称,请求头Head,请求方法是Get还是Post等等)。
同时,这个子模块还按照标准的Http协议,负责给客户端发送响应页面
比如在请求页面未发现时,connector就会给客户端浏览器发送标准的Http 404错误响应页面。
4.5:Resource模块
这个子模块包含一些资源文件,如Server.xml及Web.xml配置文件
严格说来,这个子模块不包含java源代码,但是它还是Tomcat编译运行所必需的