今天 本是怀着无比激动的心情来读源码。可是,tomcat一层套一层的 , 却搞的我晕头转向,不知所云。
还是对Tomcat的整体架构,有一定的了解后在来读吧!
另外,今天发现,tomcat webapp目录下的docs目录,有很多干货啊。
1. 接口 : Server , Service , Engine ,Host ,Context , Wrapper , Connnector,实现LifeCycle管理生命周期
组件的层次关系 : 分析server.xml , 体会这种嵌套关系
面向组件的架构!
、
以下转自 : http://yunjiechao-163-com.iteye.com/blog/2184505
目录 :
1.组件分类
2.架构的好处
3.顶级组件 server and service
4.连接器 Connector
5.容器组件
Engine
Host (虚拟主机相关)
Context
Wrapper
6.嵌套组件
Value
Realm
Executor
Listener
SessionManager
7.tomcat类加载
下面让我们来看看Tomcat 容器的整体结构:
本文的目的是覆盖这张图中所涉及的主要请求处理组件。而上图中的一些高级主题如集群和安全则不是在本文讨论的范围之内。
本图中,Service, Host, Context 以及 Wrapper 实例之后的符号 “+” 表示这些对象能存在一个或多个。例如一个 Service 可能只有一个 Engine ,但是一个 Engine 可以包含一个或多个 Host ;另外,图中旋转的圆圈代表请求处理器的线程池。
1、 组件分类
Tomcat架构采用类似俄罗斯嵌套娃娃(译注:一层套一层)的设计方式。换句话说,就是一个容器包含另一个容器,而这个被包含的容器实体反过来再包含别的实体。
而在Tomcat 中, “ 容器 ” 是对任何包含有其他组件的组件的通称,如 Server 、 Service 、 Engine 、 Host 以及 Context 都称为容器。 其中Server 和 Service 组件比较特殊些,被设计为顶级元素,代表 Tomcat 运行实例。而 Tomcat 其他所有的组件都属于这些顶级元素。其中 Engine 、 Host 以及 Contex 都被官方称为容器并依赖处理传入请求和生成适当数据响应的相关组件。
而被嵌套的组件可以作为子元素来嵌入顶级元素或其他容器之中来配置这些组件的工作方式。这其中,嵌套组件包括代表可重用的工作单元的Valve 组件、代表 Valve 链的 Pipeline 组件以及帮助特定容器建立容器管理安全的 Realm 组件。
此外,嵌套组件 中还包括用来强制servlet 中的类使用指定的规范来加载类的加载器;为每个 web 应用提供 session 管理的管理器;代表 web 应用中静态资源以及提供机制来访问这些资源的资源管理组件;以及在容器的生命周期内允许在重要的点插入自定义处理程序的监听器组件,例如当某个组件启动或停止时就可以使用监听器。
值得注意的是,并不是所有的前台组件都可以被嵌套在每个容器中。
这里还有最后一个主要组件,那就是连接器(Connector );它代表一个连接点,通过这个连接点外部客户端可以(比如网页浏览器)可以连接至 Tomcat 容器。
在我们去学习这些组件之前,让我们快速看下这些组件的大体结构:
请注意,上图只展示了每个容器的关键属性。
启动Tomcat 时,它运行的 Java 虚拟机( JVM )实例中包含一个服务器顶级元素,该元素代表了一个完整 Tomcat 服务器。一个 Tomcat 服务器通常只包含一个 Service 对象,这个对象是一种包含一个或多个连接器( Connector ,如 HTTP 、 HTTPS 连接器)的结构化元素,正是这些 Connector 通过一个 Catalina 的 Servlet 引擎来处理传入的请求。
引擎(Engine )表示 Tomcat 中处理请求的核心代码,并且它还支持在其下定义多个虚拟主机( Host )。虚拟主机允许 Tomcat 引擎在将配置在一台机器上的多个域名(如 www.my-site.com、www.your-site.com)分割开来互不干扰。
反过来,每个虚拟主机又可以支持多个web 应用部署在它下边,这就是我们所熟知的上下文对象( Context )。上下文( Context )是使用由 Servlet 规范中指定的 Web 应用程序格式表示 , 不论是压缩过的 war 包形式的文件还是未压缩的目录形式。此外,上下文一般是在 web.xml 文件中配置,并且该配置是根据 servlet 规范定义的。
从上下问角度看,在上下文中又可以部署多个servlet ,并且每个 servlet 都会被一个包装组件所包含。
至此,我们以上所说的Server 、 Service 、 Connector 、 Engine 、 Host 、 Context 元素都会通过 server.xml 配置文件在 tomcat 实例中被使用。
2、 架构的好处
这种架构有一些很实用的功能。它不仅便于组件的生命周期管理(每个组件管理生命周期并通知其子节点),而且便于在Tcomat 启动时根据从配置文件中读取的配置文件来动态组装出 Tomcat 服务器实例。 尤其是server.xml 在启动时就会被解析,其内容正是用来实例化和配置被定义的元素,并随后组装到正在运行的 Tomcat 实例中。
server.xml文件只会被读取一次,对 server.xml 的修改只有在 Tomcat 重启后才会起作用 。
同时这种架构简化配置,允许子容器继承父容器的配置。例如Realm 定义了一个可验证权限的数据存储,并且可以授权用户通过 web 应用来访问受保护资源。为了便于配置,针对引擎定义的 Realm 适用于它所有的 host 和 context 配置。同时,某一个特定的子元素如 context 也可以通过继承父类的 realm 来实现自定义 realm 。
3、 顶级组件
Server和 Service 容器组件存在的目的是尽可能的方便结构化。 Server 表示正在运行的 Tomcat 实例,可包含一个或多个 Service 子容器;其中每个 Service 代表一组请求处理组件。
Server
Server代表完整的 Tomcat 实例在 Java 虚拟集中是单例,主要是用来管理容器下各个 Serivce 组件的生命周期。
下图描述了Server 组件的关键方面。如图所示, Server 实例是通过 server.xml 配置文件来配置的;其根元素 <Server> 所代表的正是 Tomcat 实例,默认实现为 org.apache.catalina.core.StandardServer 。但是,你也可以通过 <Server> 标签的 class 属性来自定义服务器实现。
服务器可重要的一方面就是它打开了8005 端口(默认端口)来监听关闭服务命令(默认情况下命令为 SHUTDOWN )。当收到 shutdown 命令后,服务器会优雅的关闭自己。同时出于安全考虑,发起关闭请求的连接必须来自同一台机器上的同一个运行中的 Tomcat 实例。
此外,Server 还提供了一个 Java 命名服务和 JNDI 服务,可以通过这两个服务可以使用名称来注册专用对象(如数据源配置)。在运行期,单个组件(如 Servlet )可以使用对象名称来通过服务器的 JNDI 绑定服务来查找需要的对象相关信息。
虽然JNDI 实现并不是 Servlet 容器的功能,但是它属于 JavaEE 规范一部分,并且可以为 Servlet 从应用服务器或者 servlet 容器中获取所需要的信息提供服务。
虽然在一个JVM 中通常只有一个服务器实例,但是完全可以在同一台物理机器中运行多个服务器实例,每个实例对应一个 JVM 实例;这种做法将运行在一个 JVM 中的应用中的错误与其他 JVM 中应用的错误隔离开来互不影响,这也简化了维护使得 JVM 的重启与其他独立开来。这是一个共享主机环境的机制(另一种是虚拟主机机制 , 很快我们将会看到),这种机制下需要将运行在同一物理主机下的多个 web 应用隔离开来。
Service
Server代表 Tomcat 实例本身, Service 则代表 Tomcat 中一组请求处理的组件。
Server可以包含一个或多个 Service ,但每个 Service 则将一组 Connector 组件和 Engine 关联了起来。
客户端请求首先到达连接器(connector), 连接器在再将这些请求轮流传入引擎中处理,而 Engine 也是 Tomcat 中请求处理的关键组件。上图中展示了 HTTP 连接器、 HTTPS 连接以及 AJP 组件。
一般很少会修改这个元素,而且默认的Service 实例通常就足够使用了。
值得注意的是上图中可能有多个Service 实例。如图所示,一个 Service 集中了一些连接器,每个连接器监控一个指定的 IP 及端口并通过指定的协议做出响应。所以,一个关于多个服务的使用示例就是当你希望通过 IP 地址或者端口号来区分不同服务(包括这些服务中所包含的 engine 、 host 、 web 应用)时。
例如,当需要配置防火墙来为用户开放某一个服务而该主机上托管的其他服务仍然只是对内部用户可见,这将确保外部用户无法访问内部应用程序,因为对应访问会被防火墙拦截。
因此,Service 仅仅是一个分组结构,它并不包含任何其他的附加功能。
4、 连接器(Connector )
Connector是客户端连接到 Tomcat 容器的服务点它为引擎提供协议服务来将引擎与客户端各种协议隔离开来,如 HTTP 、 HTTPS 、 AJP 协议。
Tomcat有两种可配的工作模式 -- 独立模式或在同一 web 服务器中共享模式。
在独立模式下,Tomcat 会配置 HTTP 和 HTTPS 连接器,这可以使 Tomcat 看起来更像完整的 web 服务器以处理静态请求内容同时还委托 Catalina 引擎来处理动态内容。
发布时,Tomcat 为这种运作模式提供了 3 种可能实现,即 HTTP 、 HTTP1.1 以及 HTTPS 。
Tomcat中最常见的连接器为标准连接器,也就是通过 java 标准 I / O 实现的 Coyote 连接器 。
你也许希望使用一些技术实现,这其中就包括使用Java1.4 中引入的非阻塞特性 NIO ,另一方面可能是通过 APR 充分利用本地代码来优化特定的操作系统。
值得注意的是,Connector 和 Engine 不仅运行在同一个 JVM 中,而且还运行在同一个 Tomcat 服务实例中。
在共享模式中,Tomcat 扮演着对 web 服务器如 Apache httpd 和微软的 IIS 支撑的角色。这里 web 服务器充当客户端通过 Apache 模块或者通过 dll 格式 ISAPI 模块来和 Tomcat 通信。当该模块判定一个请求需要传入 Tomcat 处理时,它将使用 AJP 协议来与 Tomcat 通信,该协议为二进制协议,在 web 服务器和 Tomcat 通信时比基于文本的 Http 协议更高效。
在Tomcat 端,通过 AJP 连接器来接收 wen 服务器的请求,并将请求解释为 Catalina 引擎可处理的格式。
这种模式下Tomcat 作为来自 web 服务器的单独进程运行在自身独立的 JVM 中。
不论在哪种模式中,Connector 的基本属性都是它所需要监听的 IP 地址及端口号,以及所支持的协议。还有一个关键属性就是并发处理传入请求的最大线程数。一旦所有的处理线程都忙,那么传入的请求都将被忽略,直到有线程空闲为止。
默认情况下,连接器会监听指定物理机器上的所有IP ( address 属性默认值为 0.0.0.0 );但也可以配置为只监听某一个 IP ,这将限制它只接收指定 ip 的连接请求。
任意一个连接器接收到的请求都会被传入单一的服务引擎中,而这个引擎,就是众所周知的catalina ,它负责处理请求并生成响应结果。
引擎将生成的结果返回给连接器,连接器再通过指定的协议将结果回传至客户端。
5、 容器组件
这一小节中我们将讨论请求处理组件:引擎(engine )、虚拟主机、上下文( context )组件。
5.1、 引擎(engine)
引擎表示可运行的Catalina 的 servlet 引擎实例并且包含了 servlet 容器的核心功能。在一个服务中只能有一个引擎。同时,作为一个真正的容器, Engine 元素之下可以包含一个或多个虚拟主机。
作为请求处理的主要组件,它接收代表传入请求的对象以及输出相应结果。它主要功能是将传入请求委托给适当的虚拟主机处理。如果根据名称没有找到可处理的虚拟主机,那么将根据默认的Host 来判断该由哪个虚拟主机处理。
5.2、 虚拟主机
虚拟主机在Tomcat 中使用 Host 组件表示 。
在虚拟主机中有两个概念非常重要-- 主机的域名和根目录。
·域名:每个虚拟主机是由它注册的域名来标识的(例:www.host1.com)。域名是您预期的在客户端浏览器地址栏输入的值,对虚拟主机来说就是请求头部。一台虚拟主机的名称在包含它的引擎内必须是唯一的。
·根目录:根目录所在的文件夹包含将被部署到此主机的上下文。根目录可以是一个绝对路径,也可以是对CATALINA_BASE 来说的一个相对路径。
CATALINA_HOME 是一个环境变量,它引用了 tomcat 二进制文件的位置。通过 CATALINA_BASE 环境变量仅仅使用一个 tomcat 安装信息的二进制文件,就可以根据不同的配置运行多个 tomcat 实例(这主要由 conf 文件夹的内容决定)。
此外,使用一个CATALINA_BASE 引用的位置(和 CATALINA_HOME 不同)保持标准的二进制分配独立于您的安装。这是有好处的,使 tomcat 升级到一个新版本变得容易,而不必担心影响已经发布的 web 应用程序和相关的配置文件 。
基本概念
当涉及到主机名映射到互联网协议地址时,最简单的场景,一个给定的完全合格的主机名(FQHN) ,例如 www.swengsol.com 与映射到特定主机的IP 地址相关联。
这种方法的缺点是,主机连接到互联网是相当昂贵的。这是真实存在的,尤其是当您考虑到带宽成本、网络基础设施建设 ( 例如:数据库/邮件服务器、防火墙、不间断电源ups、容错等) 以及维护 ( 包括人员配置、管理和备份 ) ,更不用说首先要获得一个 IP 地址。
因此,许多小企业认为最好的方法是从托管服务提供商那里租赁空间和基础设施。托管服务可能是提供单个的物理服务器,它可以连接到互联网并由特定的IP 地址标识。这台物理服务器可以托管多个域名 ,每个域名代表一个提供商的客户。
什么是虚拟主机?
例如,我们假设Acme Widgets Inc. 和 Vertico LLC 拥有它们的域名, www.acme-widgets.com 和 www.vertico.com ,这些域名被托管在同一台物理服务器上。应用程序被部署到各自对应的域,并且互不干扰。
在这种情况下,这些域被称为虚拟主机,从这种意义上来讲,每一个域看起来都是一个独立的“物理主机”。然而,事实上,他们( 域 ) 仅仅是同一台物理主机上不同的逻辑分区。
5.3、虚拟主机技术
有两种常用的方法来设置虚拟主机:
·基于独立IP地址的虚拟主机服务
·基于名称的虚拟主机服务
5.3.1基于独立IP地址的虚拟主机服务
使用这种技术,每个FQHN(完全合格的主机名)被解析为一个单独的IP地址。然而,这些IP中的每一个被解析后都映射到同一台物理机器上。
您可以使用以下的机制来实现此技术:
·多宿主服务器,也就是说它安装了多个网卡(NICs),每一个网卡都分配了IP地址
·使用操作系统功能来设置虚拟网络接口,为单个物理NIC(网卡)动态分配多个IP地址
无论在哪一种情况下,缺点是我们要获得多个IP地址,而且这些地址(至少对于IPv4来说)是一种有限的资源。
Web服务器监听为这些IP地址分配的端口,当Web服务器在一个特定的IP地址检测到传入的请求时,它会生成该IP地址的响应信息。
例如,您有一个web服务器,它运行在一个特定的在80端口监听 11.156.33.345 和 11.156.33.346 IP地址请求的物理主机上。此web服务器用以下方式响应请求:当收到来自主机域名www.host1.com的请求时,则映射到11.156.33.345 IP地址;反之当收到来自主机域名www.host2.com的请求时则映射到后面的 IP地址 11.156.33.346 。
当接收到一个来自11.156.33.346 IP地址的请求时,web服务器知道它应当为ww.host2.com对应的域准备响应信息。对用户来说,这是一个完全独立的物理服务器在为他提供服务。
5.3.2基于名称的虚拟主机服务
这是一种比较新的技术,它允许您把不同的域名映射到同一个IP地址。这些都是经过注册的正常的域名,多个DNS条目将这些域名映射到同一IP地址。
HTTP 1.1协议要求每个请求必须包含一个主机头:带有完全合格的主机域名,以及用户希望连接的端口号( 如果已指定 ) 。主机上运行的 web 服务器接收到此请求,解析此请求中的主机头信息,以确定相应的虚拟主机来响应此请求。简单、而且不使用不必要的 IP 地址,基于名称的虚拟主机服务是我们的首选。
然而,当您同时使用SSL( 安全套接层 ) 和虚拟主机时,您也许不得不使用基于 IP 地址的虚拟主机服务。原因是,在特定的虚拟主机响应请求之前,协商协议要进行证书认证。这是因为: SSL 协议层位于 HTTP 协议层的下方,而且在握手消息认证完成之前,与客户端请求进行安全认证的模块无法读取 HTTP 请求头信息。
您也许可以同时使用SSL 和基于名称的虚拟主机服务,如果您的 web 服务器和客户机支持 RFC3546(传输层安全性扩展http://www.ietf.org/rfc/rfc3546.txt) 指定的服务器名称标识扩展。使用此扩展,在SSL协商期间,客户端会传输主机名称给它尝试连接的对象,从而使web服务器能够处理握手信息并为正确的主机名返回证书。
虚拟主机别名
当 web服务器解析别名信息时,例如它在主机头里看到了域名的别名,那么web服务器会把此别名当作虚拟主机的域名来处理。 例如,您把swengsol.com设置为虚拟主机域名www.swengsol.com的别名,那么在客户端url里无论是输入域名还是别名,您都会收 到来自同一个虚拟主机的响应信息。 这种方式效果不错,当一个物理主机有多个域名时,而且您不想弄乱配置文件在为每个别名创建一组条目时。
5.4、上下文(Context)
上下文或者web 应用是应用自定义代码( servlet 、 jsp )所存活的地方。它为 web 应用组织资源提供了便利。
同时context 容器为 servlet 提供了一个 ServletContext 实例。在许多方面 ,servlet 规范主要是关心这个上下文组件。例如,它规定了部署上下文的格式以及部署内容的描述符。
以下是上下文的一些重要属性:
·根目录(document base ):这个路径是指 war 包或者未压缩的项目文件所存放的目录,可以是相对的,也可以是绝对的。
·上下文路径(context path ):它是指在一个 host 下 url 中唯一标识一个 web 应用的部分。它帮助 host 容器来判断该由哪一个已部署的上下文来处理传入的请求。
也许你可能配置了默认context ,它可以在找不到匹配的 context 的情况下来处理传入请求。该默认 context 可以通过将其上下文路径配置为空来标记的,因此,可以通过只有主机名的路径来访问它(译注:如 http://localhost:8080/ 来访问)。并且该 context 已被 tomcat 默认定义为根目录下的 ROOT 目录。
·自动重加载(automic reload ):上下文中的资源会被 tomcat 监控,一旦资源发生改变 Tomcat 就会自动重新加载资源文件。虽然该功能在开发过程中非常有用,但是在生产环境这个操作代价非常高,通常需要重启产品应用。
Context配置
Context是唯一的,这主要是因为它的配置包含多个选项。而我们之前已经注意到的 conf/server.xml 是用来配置 Tomcat 实例中一些全局性的参数。虽然在这个文件中可以配置 context 相关 的东西,但是不推荐这样做。
相反,Tomcat 推荐大家将 context 相关的配置从 server.xml 中提取出来,配置到上下文段文件中,该文件会被 Tomcat 监控并且可以在运行过程中重新加载。
请再次注意,server.xml 只有在启动时被加载一次。
同时需要确保在context 中配置一个独立明确的 host 和 engine ,因为 Tomcat 会在 CATALINA_HOME/conf/// 目录下查找 context 相关配置。而该目录下为特定主机配置的上下文段文件则是以名称 .xml 命名。
默认情况下,会有一个引擎Catalina 和一个名称为 localhost 的主机,对应的工作目录为 CATALINA_HOME/conf/Catalina/localhost 。但是该目录也可以是有效域名,如 www.swengsol.com ,那么对应目录就是 CATALINA_HOME/conf/Catalina/www.swengsol.com 。
另外,context 片段也可以在 war 或部署目录中被包含在 META-INF 目录下。这种情况下, context 文件名称必须为 context.xml 。
此外,Context 还可以被配置在 web 应用描述符文件 web.xml 中。虽然这个片段文件是 Tomcat 专用的,但是由于该描述符是通过 Servlet 规范来描述的,因此它也适用与 JavaEE 下的其他轻量级 servlet 容器。
包装器(Wrapper )
包装器wrapper 对象是 context 容器的子容器,表示一个单独的 servlet (或者由 jsp 文件转换而来的 servlet ),他就不能在包含其他容器了。 它之所以称为包装器是因为它包装了java.servlet.Servlet 实例。
这是容器层次结构的最底层,添加任何子类都会导致异常 。
同时包装器还对它所包装的servlet 负责,包括加载、实例化 servlet 以及调用控制 servlet 生命周期相关的函数 ,如init() 、 service() 和 destroy() 方法。
此外包装器还通过它基本的Valve 来调用和其包装的 servlet 相关的过滤器。
嵌套组件
这些组件是针对Tocmat做的特定实现,他们的主要目的是使各种Tomcat容器可以完成各自的工作。
1、 阀(Valve)
valve是处理元素,它可以被包含在每个Tomcat容器的处理路径中--如engine、host、context以及servelt包装器。若要增加Valve到Tomcat容器则需要在server.xml中使用<Valve>标签。在server.xml中这些标签的执行顺序与其物理顺序相同。
而在Tomcat中也分布这大量预先编译好的valve。包括:
· 在请求日志元素中将请求(如远程客户端ip地址)写入日志文件或数据库时
· 根据远程客户端ip或主机名来控制某一特定web应用的访问权限时
· 记录每个请求和响应头信息日志时
· 在同一个虚拟主机下为多个应用配置单点登录时
如果以上这些都不能满足你的要求,那么你可以通过继承org.apache.catalina.Valve来实现自定义的Valve并将其配置到对应服务中。
// 管道过滤器的软件体系结构
但是对于一个容器来说,它并不会持有某个单独valve的引用;相反,它会持有一个称作管道(Pipeline)的单一实体的引用,并用这个管道来表示与该容器所关联的一系列valve 。
当一个容器被调用来处理一个请求时,它会委托与其关联的管道来处理对应请求。
在管道中,这些valve则是基于他们在server.xml中的定义作顺序排列。其中排在队列中排在最后的valve被称为管道的基本valve,该valve用来完成去执行容器的核心功能的任务。
与单个valve不同,管道在server . xml不是一个明确的元素,而是含蓄的按照valve在给定容器中所定义的顺序组成。
并且在管道中,每个valve都知道其下一个valve;在它执行完前置处理以后,接下来它会调用链中的下一个valve,当该调用返回以后,它会在return之前执行他自身的处理任务。
这种方式和servlet规范中的过滤器链所做的事情非常相似。
在这幅图中,当接收到传入请求时引擎所配置的valve首先被触发。其中引擎中基本的valve负责确定目标主机委托该主机来处理;接下来目标主机( www.host1.com )的valve被按顺序触发。而该主机的基本valve则又决定了目标context(在这里是Context1)并且委托该context来处理该请求。最后Context1中所配置的valve被触发,然后通过context中配置的基本valve委托给适当的包装器来处理;而包装器的基本valve又将处理转交至被包装的servlet来处理。
处理完成以后,响应结果会按照以上的路径反方向返回。
由于Valve就成了Tomcat服务器实现中的一部分,并且可以为开发者提供一种方式将自定义的代码注入到处理请求的servlet容器中。因此,自定的valve类文件需要发布到CATALINA_HOME/lib目录下而不是应用的发布目录WEB-INF/classes 。
由于它们并不是servlet规范中的部分,所以valve在企业级一用中属于不可移植元素。因此,如果已经依赖了一个特定的valve时,你必须在不同的应用服务器上找到对等的选择方案。
还有很重要的一点就是,为了不影响请求处理的效率必须要保证valve的代码高效执行。
2、 Realm
容器管理安全方面的工作通过容器处理应用程序的身份验证和授权方面来解决。
身份验证存在的主要任务就是确保用户所说的就是她自己,而授权的主要任务是决定一个用户是否可以在某个应用下执行特定操作。
由容器来管理安全的优势是可以通过应用的发布者直接来配置安全措施。也就是说,为用户分配密码以及为用户分配角色都可以用户配置来完成,而这些配置也可以在修改任何代码的情况下来供多个web应用共用。
应用管理安全
还有一种可选方案就是通过应用来管理安全问题。这种情况下,我的web应用程序代码就是唯一的仲裁者来决定用户在我们的应用下是否有访问特定功能或资源的权限。
想要使容器来管理安全问题起作用,我们需要组装一下组件:
· 安全约束 :在我们的web应用部署描述器web.xml中,我们必须确定限制资源访问的url表达式以及可以访问这些资源的用户角色。
· 凭证输入机制 :在web.xml部署文件中,我们需要指定容器应该如何提示用户通过凭证来验证。这通常是通过弹出一个对话框提示用户输入用户名和密码来完成,但也可以配置使用其他机制,如一个定制的登录表单等。
· Realm :这是一个数据存储机制来保存用户名、密码以及角色来对用户所提供的凭证信息进行检查。它可以是一个简单的xml文件,一个通过JDBC API来访问的关系型数据库中的一张表或者是可以通过JNDI API访问的轻量级目录访问协议服务器(LDAP)。正是Realm为Tomcat提供了一致的访问机制来访问这些不同的数据源。
以上这三种组件在技术上是相互独立的。基于容器安全的威力就在于我们可以根据我们自身的安全情况从这几种方式中选出适合的一种或几种方式来混合使用。
至此,当一个用户请求一个资源时,Tomcat将检查对所请求的资源是否已经存在了安全限制。对于存在限制的资源,Tomcat将自动要求用户提供身份凭证并通过所配置的Realm来检查用户所提供凭证是否符合。只有在用户所提供的凭证信息通过了验证或者用户的角色在可访问资源的配置之列才能访问对应资源。
3、 执行器 Executor
这是从 tomcat 6.0.11 版本开始,新增的一个组件。此组件允许您配置一个共享的线程池,以供您的连接器使用。您的连接器可能使并发线程的数量达到上限。请注意,此限制同样适用于:即使一个特定的连接器没有用完为它配置的所有线程。
4、 监听器
每个主要的tomcat 组件都实现了 org.apache.catalina.Lifecycle 接口。实现了该接口的组件注册到监听器,然后该组件的生命周期事件被触发,比如该组件的启动和停止都会被监听。一个监听器实现了 org.apache.catalina.LifecycleListener 接口,也实现了该接口的 lifecycleEvent() 方法,监听器捕捉到一个 LifecycleEvent 表示事件已经发生。这就给您提供了一个机会:把您自定义的进程注入到tomcat 的生命周期。
5、会话管理器
会话让使用无状态HTTP协议的应用程序完成通信。会话表示客户端和服务器之间的通信,会话功能是由 javax.servlet.http.HttpSession 的实例实现的,该实例存储在服务器上而且与一个唯一的标识符相关联,客户端在与服务器的每次交互中根据请求中的标识符找到它的会话。一个新的会话在客户端请求后被创建,会话一直有效直到一段时间后客户端连接超时,或者会话直接失效例如客户退出访问服务器。
上图显示了一个非常简单的 tomcat 会话机制视图。 Catalina 引擎 (engine) 使用了组件org.apache.catalina.Manager 去创建、查找、注销会话。该组件负责管理为上下文创建的会话以及会话的生命周期。会话管理器(Manager)把会话存放在内存中,但是它支持在服务器重启时恢复会话。当服务器停止时,会话管理器把所有活动的会话写入磁盘,当服务器重新启动时把会话重新加载到内存。
一个<Manager>必须是 <Context>的子节点,而且<Manager>负责管理与web应用程序上下文相关的会话。
会话管理器管理这样的属性:例如用来生成会话标识符的算法,每秒钟检查过期会话的频率,支持的活动会话的最大数目,以及持久化会话的文件。
会话管理器实现了这样的功能:为会话提供持久化存储,例如一个文件或一个JDBC数据库。
6、 加载器
这个组件是一个给定的web 应用程序的类加载器。 Java 中的类加载器是一个神秘的实体。简而言之,类加载器负责加载、解释 Java 类编译后的字节码。
一个Java 类的字节码可能存放在各种不同的位置,最常见的是在本地文件系统或网络中。类加载器的主要任务是:抽象字节如何被获取以及如何重组到内存中的类的过程。
7、 委托 (代理) 模型
自从Java 2 以来,类加载机制使用了委托模型, JVM 中的类加载器被组织成了父 --- 子层次结构。据介绍,每个类加载器首先把查找和加载一个类的任务委托给它的父级,在它自己尝试做这个任务之前。
这种委托机制确保: 没有应用程序可以加载一个有恶意的系统类( 例如 java.lang.Object ),它可能危及在 JVM 中运行的应用程序的完整性 。
类加载器层次结构的顶层是Bootstrap 类加载器,它也叫原始类加载器,它是原生代码而且是 JVM 的一部分。作为 JVM 的一部分可以保证:至少有一个可以依靠的类加载器,去加载 Java 的核心类 ( 例如 java.lang.Object )。这个类加载器 (Bootstrap ) 负责从 Java 核心包 ( 例如 java.lang 或 java.util )里加载类文件。在 SUN 的 JVM 中,这些核心类文件存放在 JAVA_HOME/jre/lib/rt.jar 。 Bootstrap 类加载器的独特之处在于:它是层次结构树的根节点,因此它没有父类加载器。
层次结构的下一层是Extension 类加载器,它在 SUN 的 JVM 中是一个 java.net.URLClassLoader ,它监控 JAVA_HOME/jre/lib/ext 文件夹扩展 JARs 。放在此文件夹下的任何 JARs( 包括类路径之外的 ) 都会被自动加载。
层次结构最底层是系统类加载器( 应用程序类加载器 ) ,它在 SUN 的 JVM 中也是一个 URLClassLoader 。它监控 CLASSPATH 里描述的文件夹、JARs 。这个类加载器负责加载应用程序的主类。
如果一个普通的应用程序需要加载一个类( 例如 java.lang.String ),它首先会询问应用程序类加载器去加载那个类。应用程序类加载器会委托给它的父级 Extension 类加载器, Extension 类加载器委托给它的父级 Bootstrap 类加载器, Bootstrap 类加载器然后在 rt.jar 里加载 String.class 文件并且使它成为一个可用的 java.lang.Class 实例。
如果需要加载一个应用程序特定的类文件( 例如 com.swengsol.UserModel.class ),那么委托过程和前面一样。然而这次, Bootstrap 类加载器在 rt.jar 里没能加载到此类文件,现在轮到 Extension 类加载器同样没能加载到此类文件。最后,应用程序类加载器在 CLASSPATH 里找到了此类文件。然后这个类文件被加载并成为一个JVM 可以使用的实例。
每个类加载器里都有缓存,所以每个类加载器首先要检查自己的缓存看是否已经加载了类文件。如果找到了,则直接返回类文件。
在我们前面的示例中,如果应用程序需要另一个String 类,那么 Bootstrap 类加载器则直接返回在它缓存里的 String 类实例。
Tomcat类加载
8、 标准覆盖机制
J2SE 1.4 and 1.5 都包含了一个XML 处理解析器的 Java API 。 Bootstrap 类加载器加载这个解析器的类文件,所以这个解析器会优先于任何一个安装在 CLASSPATH 里的解析器 被加载,即使您已经安装了新版本的解析器。标准覆盖机制允许您重写 JAVA_HOME/lib/endorsed 文件夹里某些特定的类( 例如 CORBA 和 JAXP 类) 。 Bootstrap 类加载器将会优先加载这些类。想详细了解此机制,请访问 http://java.sun.com/j2se/1.5.0/docs/guide/standards/ 。
有关类加载的一些有趣的注意事项 如下所示:
·只有当一个类具有包名、类名以及加载此类文件的类加载器的实例的时候,一个类才会被认为完全合格。换句话说,同样的类被两个不同的类加载器加载,会被认为是两个不同的类。即使在同一个JVM中,这已影响到这个类实例的分配、静态属性或单例的处理。
·一个类加载器只能看到位于它层次结构上面的类的目录。例如,一个Extension文件夹里的 JAR不能使用应用程序类路径里的类文件。这是因为,Extension文件夹里的类只能看到Extension类加载器以及Bootstrap 类加载器可以加载的类。
·当一个类的代码中引用了另一个类时,加载引用类的类加载器同样也会加载被引用的 类,这称为自定义类加载器。一个类的自定义类加载器可以使用Class.getClassLoader()来获得。
·每个线程都有一个上下文类加载器,使用Thread.currentThread().getContextClassLoader()可以查看到。每次一个新的线程被创建,这个新线程的上下文类加载器会被设置到它的创建线程。main()线程的类加载器是应用程序类加载器,它会自动向下传播到每个工作线程,除非您通过调用 Thread.currentThread().setContextClassLoader()进行干预。
9、JavaEE类加载(为什么Tomcat要自定义三个类加载器?)
在JavaEE的范畴里这种模式显得有些别扭。
首先,Servlet容器需要为web应用提供一个限制环境。
如果某个servlet直接使用系统类加载器(System Calss Loader),那么该servlet会看到启动Tocmat时所使用JVM命令中所使用的path下的所有类。(为什么这么说,因为tomcat也可以看成一道程序,当你启动Tomcat的时候,实质上也是调用的Bootstap中的main方法,所以所以的servlet可以看成这个main方法依赖的对象,所以要加载,如果直接通过系统的加载器,那么由上面的注意事项可知,对象间,彼此可见的!) 这是有潜在安全风险的,因为恶意应用(部署在同一个主机商的主机上的应用)可能被允许加载到其兄弟节点上的web应用的类。正是因为如此,每个web应用必须有自己的类加载器 ,而该类加载器位于类加载器树的根节点并且可以优先加载在web应用的目录WEB-INF/classes以及WEB-INF/lib下所发现的类。
当所请求加载的类是java标准时,该自定义类加载器将只委托其父类加载器来加载;如果一个web应用还需要其他的类,那么这个自定义类加载器不是去委托其父类去加载,而是首先检查WEB-INF/classes以及WEB-INF/lib中是否包含该类。只有在这两个目录都找不到时,该类加载器才会委托给其父类加载器来加载,而父类加载器的的加载遵循标准委托模式。(译注:类加载相关知识请看文章末尾)。
Tomcat的额外类加载器
在启动过程中,Tomcat首先通过清除CLASSPATH并将其重新指向CATALINA_HOME/bin/bootstrap.jar(Tomcat启动所需的类)、tomcat-juli(日志功能)、tools.jar(jsp编译功能)来屏蔽系统类加载器(System ClassLoader)。这使得系统类加载器只有在加载一小部分Tomcat特殊类时才起作用。
同时Tomcat也改变了支持目录并将其重新指向CATALINA_HOME/endorsed目录。
在其自定类加载器之下,Tomcat又增加了它自己的类加载器,这其中包括server类加载器(Server class loader)、共享类加载器(Shared class loader)、通用类加载器(Common class loader)以及每个部署的应用程序都有的一个web应用程序类装入器。
当一个web应用需要加载一个类时,请求首先发送至web应用的类加载器,该加载器负责加载web应用中WEB-INF/classes和WEB-INF/lib两个目录下的类。
web应用类加载器首先会请求系统类加载器来允许各层类加载器可以查找所有java核心类。当所请求的类未找到到时,该web应用类加载器会尝试从自身类库中定位所请求的类;如果仍然未找到,它将委托通用类加载器或者如果有共享类加载器时委托共享类加载器来加载。
共享类加载器和服务器类加载器默认情况下是没有安装的,但是我们可以通过编辑 CATALINA_HOME/conf/catalina.properties文件通过增加shared.loader和server.loader来开启共享类加载器以及server类加载器;而Common类加载器会监控CATALINA_HOME/lib目录的内容,该下为一些蝉蛹的jar包,比如servlet-api.jar, jasper.jar, coyote.jar, and jsp-api.jar。此外,位于共享目录下的类将对所有web应用可见,但不是Tomcat的内部类,而放在服务器的loader目录的类将只对Tomcat内部类可见。
10、 web应用中的类重载
使Tomcat支持类重载的web应用特定类加载器。
当context需要被新部署或者当一个类需要被重载时(例如当一个被重新编译过的类文件加入到WEB-INF/classes目录时),整个web应用的类加载器都会被抛弃,然后创建一个新的实例分支来加载该web应用的所有类。该新的类加载就是用来为之后的请求服务的。
11、 Logger
server.xml中的logger元素在Tomcat5.5中就已经被弃用;所以在Tomcat6中是基于Java1.4中所引入的日志API来生成日志的。
然而Java日志只能在整个JVM级别来进行配置,而不能对每个类加载器单独配置。为了使每个web应用程序都能允许不同的配置文件,Tomcat实现了自定义的Java日志,这就是我们所熟知的JULI,该实现位于CATALINA_HOME/bin/tomcat-juli.jar中。
全局的配置文件CATALINA_HOME/conf/logging.properties控制着日志设置。此外,每个web应用也可以有自己的日志配置文件WEB-INF/classes/logging.properties。
如上图所示,日志由一下组件组成:
● Logger :所有的日志请求会进入Logger对象。这些对象都按层次排列,其根在根logger;同时这种层次结构也反映了包结构。在该层次中属性可以被绑定到任何级别上,而且Logger的子类也会继承器父类属性。
● Handler :指定日志消息应该发送何处。可选的有ConsoleHandler(将日志写入控制台)、FileHandler(将日志写入文件)、SocketHandler(将日志写入Tcp socket)。
● Level (日志级别):日志级别有其中:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST、OFF(禁用)、ALL(记录所有信息),每种级别决定了需要记录何种类型的消息。
● Formatter (日志格式化):该元素决定了信息以什么格式展示。Tomcat提供了SimpleFormatter、XMLFormatter两种格式化工具。
12、 资源(Resource)
和web应用上下文相关的资源包括静态资源,如类文件、HTML、JSP、CSS文件。这些资源可能以不同的格式存在。默认情况下,Tomcat支持从war格式的压缩文件(译注:war包)或者解压过的war中查找资源文件。
可想而知,一个上下文中的资源也可能从替代存储机制中访问,比如JDBC数据库。而资源组件使之成为可能。
Tomcat同时也提供基于目录服务的JNDI API,该服务可支持从未知存储方式中读取资源。
总结
这里讲了很多,现在我们需要做的就是使自己理解Tomcat架构总览中所讲解的东西。在这篇文章里,我们讨论了一些Tomcat的核心组件,看到了一个运行中的Tomcat实例是如何由各种顶级组件、连接器(Connector)以及嵌套组件构建起来的。
Tomcat通过各个组件构建起来,组件间通过管道过滤器的架构(pipeLine and value)联系起来!
1. 本文由程序员学架构翻译
2. 本文译自 https://www.packtpub.com/books/content/overview-tomcat-6-servlet-container-part-1
3. 转载请务必注明本文出自 :程序员学架构(微信号: archleaner )
4. 更多文章请扫码:
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com