Tomcat架构

Tomcat历史

Tomcat最初有sun公司的架构师James Duncan Davidson开发,名称“JavaWebServer” 1999与Apache软件基金会旗下的JServ项目合并,也就是Tomcat。2001 tomcat4.0 里程碑式的版本。完全重新设计了其架构,并实现了Servlet2.3和JSP 1.2规范。到目前,Tomcat已经成为成熟的Servlet容器产品,并作为 JBoss等应用的服务器的内嵌Servlet容器。

总体结构(结构演变理解)

1、最简单的结构

Server
接受请求并解析,完成相关任务,返回处理结果通常情况下使用Socket监听服务器指定端口来实现该功能,一个最简单的服务设计如下方法:

  • Start():启动服务器,打开socket连接,监听服务端口,接受客户端请求、处理、返回响应
  • Stop():关闭服务器,释放资源
    缺点:请求监听和请求处理放一起扩展性很差(协议的切换 tomcat独立部署使用HTTP协议,与Apache集成时使用AJP协议)

改进:网络协议与请求处理分离

2、Connector和Container

一个Server包含多个Connector(链接器)和Container(容器)

Connector:开启Socket并监听客户端请求,返回响应数据;Container:负责具体的请求处。

缺点:Connector接受的请求由那个Container处理,需要建立映射规则

改进:一个Server可以包含多个Service,每一个Service都是独立的,他们共享一个JVM以及系统类库。

3、Server包含多个Service

一个Service负责维护多个Connector和一个Container,这样来自Connector的请求只能有它所属的Service维护的Container处理。

在这里Container是一个通用的概念,为了明确功能,并与Tomcat中的组件名称相同,可以将Container命名为Engineer

4、Engine、Context

在Engine容器中需要支持管理WEB应用,当接收到Connector的处理请求时,Engine容器能够找到一个合适的Web应用来处理,因此在上面设计的基础上增加Context来表示一个WEB应用,并且一个Engine可以包含多个Context。

缺点:应用服务器需要将每个域名抽象为一个虚拟主机,

5、Host、Wrapper

通常在一个主机下,提供多个域名的服务,因此引入Host

在一个web应用中,可以包含多个Servlet实例来处理来自不同的链接请求,因此我们还需要一个组件概念来表示Servlet定义,即Wrapper。

20200404205355

在前面的Container容器中,Engine、Host、Context、Wrapper是一类组件,这类组件的作用就是处理接收客户端的请求并且返回响应数据,并且有可能请求到Engine容器,Engine容器委托给子容器Host处理。

20200404210742

使用Container代表容器,Engine、Host、Context、Wrapper都是Container的子容器,Container可以维护子容器。backgroundProcess()方法针对后台处理,并且其基础抽象类(ContainerBase)确保在启动组件的同时,异步启动后台处理。

容器之间的组合关系是一种弱依赖,用虚线表示。

每一个组件都有启动、停止等生命周期方法,拥有生命周期的特征。所以定义一个通用的LifeCycle接口,

20200404210830

20200404205607

架构

连接器Connector

1
2
3
4
监听服务器端口,读取来自客户端的请求
使用指定的协议解析请求数据
根据请求地址匹配正确的容器解析处理
讲响应返回给客户端

连接器需要完成 3 个高内聚的功能:

  • 网络通信。
  • 应用层协议解析。
  • Tomcat Request/Response 与 ServletRequest/ServletResponse 的转化。

因此 Tomcat 的设计者设计了 3 个组件来实现这 3 个功能,分别是 EndPoint、Processor 和 Adapter。

Endpoint 和 Processor 放在一起抽象成了 ProtocolHandler 组件,连接器用 ProtocolHandler 来处理网络连接和应用层协议。

20200404215946

EndPoint 是一个接口,它的抽象实现类 AbstractEndpoint 里面定义了两个内部类:Acceptor 和 SocketProcessor。其中 Acceptor 用于监听 Socket 连接请求。SocketProcessor 用于处理接收到的 Socket 请求。

EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。

20200404220033

容器

Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。

Context 表示一个 Web 应用程序;Wrapper 表示一个 Servlet,一个 Web 应用程序中可能会有多个 Servlet;Host 代表的是一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序;Engine 表示引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。

请求定位 Servlet 的过程:Tomcat 会创建一个 Service 组件和一个 Engine 容器组件,在 Engine 容器下创建两个 Host 子容器,在每个 Host 容器下创建两个 Context 子容器。由于一个 Web 应用通常有多个 Servlet,Tomcat 还会在每个 Context 容器里创建多个 Wrapper 子容器。

20200404220250

一个请求在 Tomcat 中流转的过程:

20200404220834

startup.sh 启动 tomcat 的过程:

20200404220908

Coyote

CyoOt是Tomcat的一个连接器组件,支持ajp、http1.1、spdy三种协议。

1、http

默认情况下,HTTP连接器使用Tomcat进行安装,并准备使用。该连接器具有最低的等待时间和最佳的整体性能。
对于集群,必须安装支持Web会话粘性的HTTP负载平衡器,以将流量引导到Tomcat服务器。Tomcat支持Adache HTTP Server 2 .x上的MODJOXER代理,并在默认情况下包含在Apache HTTP服务器2.2中作为负载均衡器。应该注意的是,HTTP代理的性能通常低于AJP的性能,所以AJP聚类通常是优选的。

2、ajp

当使用单个服务器时,在Tomcat实例前面使用 native webserver时的性能大部分时间都比具有默认HTTP连接器的独立Tomcat更差,即使Web应用程序的很大一部分是由静态文件构成的。如果出于任何原因需要与本机WebServer集成,AJP连接器将提供比代理HTTP更快的性能。从Tomcat的角度来看,AJP聚类是最有效的。它在功能上等同于HTTP集群。

Jasper

Tomcat的JSP引擎。解析JSP文件编译成java代码的servlet(可以处理卡特琳娜)。在运行时,Jasper检测到对JSP文件的更改并重新编译它们。

Pipeline和Value

对于应用服务器来说,增强各组件的扩展性以及灵活性是非常重要的,Tomcat采用职责链模式来实现每个Container组件处理请求的功能。

Pipeline代表职责链,后者表示阀门,具体的处理过程,每条线路上包含哪些操作,操作按照顺序一个个执行。Tomcat通过这种方式来决定每个容器的执行过程。

每一个容器都有一个 Pipeline 对象。

20200404220801

Tomcat监听客户端的请求,获得请求后交个各个组件去处理,返回响应数据到客户端,并且Tomcat能支持HTTP、AJP等协议。并且在扩展性、可用性上有着非常优秀的设计。

启动流程

tomcat的启动流程很标准化,入口是BootStrap,统一按照生命周期管理接口Lifecycle的定义进行启动。首先,调用init()方法逐级初始化,接着调用start()方法进行启动,同时,每次调用伴随着生命周期状态变更事件的触发。

20200405225231

请求流程

如果请求为:http://localhost:8080/index.jsp

  1. 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector连接器组件获得
  2. Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应
  3. Engine获得请求localhost:8080/index.jsp,匹配它所有虚拟主机Host
  4. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
  5. localhost Host获得请求/index.jsp,匹配它所拥有的所有Context
  6. Host匹配到路径为/的Context,path=”/“的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
  7. Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类
  8. 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
  9. Context把执行完了之后的HttpServletResponse对象返回给Host
  10. Host把HttpServletResponse对象返回给Engine
  11. Engine把HttpServletResponse对象返回给Connector
  12. Connector把HttpServletResponse对象返回给客户browser

    类加载机制

    tomcat自定义了三种类加载器。commonLoader、catalinaLoader、sharedLoader。
  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

jvm加载器加载机制是双亲委派模型,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。

Tomcat8 和 Tomcat6比较大的区别是 :Tomcat8可以通过配置 不打破双亲委托 ,类的加载顺序略不同
20200405170917

总结

1
2
3
4
5
6
7
8
9
Server:代表整个Servlet容器
Service:代表Connector集合、Container容器。多个Connector对应一个Container
Container:通用的容器。
Connector:链接器
Engine:Servlet引擎
Host:主机映射
Context:webAPP
Wrapper:代表Servlet
Executor:线程池

以上的配置在Server.xml文件中