Tomcat相关原理部分
??此部分是参考B站视频讲解《黑马程序员Java进阶教程Tomcat核心原理解析》以及配套教材《Tomcat架构解析·刘光瑞著》进行整理。
Tomcat的目录结构
目录 | 目录下文件 | 目录下文件 |
---|
bin | / | 存放Tomcat的启动、停止等批处理脚本文件 | | startup.bat ,startup.sh | 用于在windows和linux下的启动脚本 | | shutdown.bat ,shutdown.sh | 用于在windows和linux下的停止脚本 | conf | / | 用于存放Tomcat的相关配置文件 | | Catalina | 用于存储针对每个虚拟机的Context配置 | | context.xml | 用于定义所有web应用均需加载的Context配置,如果web应用指定了自己的context.xml,该文件将被覆盖 | | catalina.properties | Tomcat 的环境变量配置 | | catalina.policy | Tomcat 运行的安全策略配置 | | logging.properties | Tomcat 的日志配置文件, 可以通过该文件修改Tomcat 的日志级别及日志路径等 | | server.xml | Tomcat 服务器的核心配置文件 | | tomcat-users.xml | 定义Tomcat默认的用户及角色映射信息配置 | | web.xml | Tomcat 中所有应用默认的部署描述文件 | lib | / | Tomcat 服务器的依赖包 | logs | / | Tomcat 默认的日志存放目录 | webapps | / | Tomcat 默认的Web应用部署目录 | work | / | Web 应用JSP代码生成和编译的临时目录 |
Tomcat整体架构
http 服务器请求处理
??浏览器发给服务端的是一个HTTP格式的请求,HTTP服务器收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同的Java类来处理。 ??图1表示HTTP服务器直接调用具体业务类,它们是紧耦合的。当服务器接受到 http 请求时,会 if 判断 请求的是什么业务(比如通过 url 判断),然后调用业务类做出对应的响应 ??图2,HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过Servlet接口调用业务类。因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类解耦的目的。而Servlet接口和Servlet容器这一整套规范叫作Servlet规范。Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功能。作为Java程序员,如果我们要实现新的业务功能,只需要实现一Servlet,并把它注册到Tomcat(Servlet容器)中,剩下的事情就由Tomcat帮我们处理了。
Servlet容器工作流程
??为了解耦,HTTP服务器不直接调用Servlet,而是把请求交给Servlet容器来处理,当客户请求某个资源时,HTTP服务器会用一个ServletRequest对象把客户的请求信息封装起来,然后调用Servlet容器的service方法,Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成初始化,接着调用Servlet的service方法来处理请求,把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
tomcat 整体架构
Tomcat要实现的两个核心功能: ??处理Socket连接(网络编程),负责网络字节流与Request和Response对象的转化。 ??加载和管理Servlet,以及具体处理Request请求。 因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。浏览器发起一个请求后,这个请求会被连接器接收到,连接器将接收到的Socket请求转化为ServlerRequest,然后把ServlerRequst转发给容器,容器拿到ServlerRequest之后,就会根据请求的路径去容器内找的对应的Servler去执行具体的业务逻辑,执行完之后会形成一个ServlerResponse对应返回给连接器,连接器拿到ServlerResponse对象后会解析并通过Socket(TCP)返回响应。
连接器-Coyote
??Coyote 是Tomcat的连接器框架的名称 , 是Tomcat服务器提供的供客户端访问的外部接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应 。 ??Coyote 封装了底层的网络通信(Socket 请求及响应处理),为Catalina 容器提供了统一的接口,使Catalina 容器与具体的请求协议及IO操作方式完全解耦。Coyote 将Socket 输入转换封装为 Request 对象,交由Catalina 容器进行处理,处理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写入输出流 ??Coyote 作为独立的模块,只负责具体协议和IO的相关操作, 与Servlet 规范实现没有直接关系,因此即便是 Request 和 Response 对象也并未实现Servlet规范对应的接口, 而是在Catalina 中将他们进一步封装为ServletRequest 和 ServletResponse??在Coyote中,Tomcat支持的多种I/O模型和应用层协议,具体包含了以下IO模型和应用层协议: Tomcat 支持的IO模型(自8.5/9.0 版本起,Tomcat 移除了 对 BIO 的支持改为使用NIO)
IO模型 | 描述 |
---|
NIO | 非阻塞IO,采用JAVA NIO实现 | NIO2 | 异步IO,采用JDK7最新的NIO2类库实现 | APR | 采用Apache可移植运行库实现,是C/C++编写的本地库,如果选择此方案需要单独安装APR库 |
Tomcat 支持的应用层协议:
应用层协议 | 描述 |
---|
HTTP1.1 | 大部分web应用采用的协议 | HTTP2 | HTTP2大幅度提升了web性能,自tomcat 9版本以后支持 | AJP | 用于和Web服务器(Apache)集成,以实现对静态资源的优化和集群部署,当前支持AJP/1.3 |
??Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作Service组件。这里请你注意,Service本身没有做什么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。Tomcat内可能有多个Service,这样的设计也是出于灵活性的考虑。通过在Tomcat中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。 Coyote中的连接组件分为四部分: EndPoint ?? Coyote 通信端点,即通信监听的接口,是具体Socket接收和发送处理器,是对传输层的抽象,因此EndPoint用来实现TCP/IP协议的。 ??Tomcat 并没有EndPoint 接口,而是提供了一个抽象类AbstractEndpoint , 里面定义了两个内部类:Acceptor和SocketProcessor。Acceptor用于监听Socket连接请求。SocketProcessor用于处理接收到的Socket请求,它实现Runnable接口,在Run方法里调用协议处理组件Processor进行处理。为了提高处理能力,SocketProcessor被提交到线程池来执行。而这个线程池叫作执行器(Executor)。 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, AjpNio2Protocol , Http11NioProtocol ,Http11Nio2Protocol ,Http11AprProtocol。我们在配置tomcat/conf/server.xml 时 , 至少要指定具体的ProtocolHandler , 当然也可以指定协议名称 , 如 : HTTP/1.1 ,如果安装了APR,那么将使用Http11AprProtocol , 否则使用 Http11NioProtocol 。 Adapter ??由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类来“存放”这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类。但是这个Request对象不是标准的ServletRequest,也就意味着,不能用TomcatRequest作为参数来调用容器。Tomcat设计者的解决方案是引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用CoyoteAdapter的Sevice方法,传入的是TomcatRequest对象,CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用容器的Service方法。
容器-Catalina
??Tomcat是一个由一系列可配置的组件构成的Web容器,而Catalina是Tomcat的servlet容器。 Catalina 是Servlet 容器实现,包含了之前讲到的所有的容器组件,以及后续章节涉及到的安全、会话、集群、管理等Servlet 容器架构的各个方面。它通过松耦合的方式集Coyote,以完成按照请求协议进行数据读写。同时,它还包括我们的启动入口、Shell程序等。 Tomcat 本质上就是一款 Servlet 容器, 因此 Catalina 才是 Tomcat 的核心 , 其他模块都是为Catalina 提供支撑的。 比如 : 通过Coyote 模块提供链接通信,Jasper 模块提供JSP引擎,Naming 提供JNDI 服务,Juli 提供日志服务。(Servlet 可以理解为 一种规范) ??Catalina 的主要组件结构如下:Catalina负责管理Server,而Server表示着整个服务器。Server下面有多个服务Service,每个服务都包含着多个连接器组件Connector(Coyote 实现)和一个容器组件Container。在Tomcat 启动的时候, 会初始化一个Catalina的实例。 ??Tomcat的Container中设计了4种容器,分别是Engine、Host、Context和Wrapper。这4种容器不是平行关系,而是父子关系。, Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性。 ??其中,一个Tomcat只有一个Catalina,一个catalina有多个Service,一个Serivce又包含了多个Connector和一个Container,一个Containner中只有一个Engine,Engine是整个Container容器的引擎,用来管理多个站点,一个Engine中可以有多个Host,一个Host代表一个站点,一个Host里有可以包含多个Context,Context就是一个Web应用,另外Host中还包含了Realm等用于控制访问安全,后面讲Web.xml配置和tomcat-user.xml配置时会讲到,一个Web应用中可以包含多个Wrapper,Wrapper就是Servlet。 我们也可以再通过Tomcat的server.xml配置文件来加深对Tomcat容器的理解。Tomcat采用了组件化的设计,它的构成组件都是可配置的,其中最外层的是Server,其他组件按照一定的格式要求配置在这个顶层容器中。 ??Tomcat的容器是具有父子关系的,形成一个树形结构,Tomcat就是用组合模式来管理这些容器的。具体实现方法是,所有容器组件都实现了Container接口,因此组合模式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的是最底层的Wrapper,组合容器对象指的是上面的Context、Host或者Engine。Container接口扩展了LifeCycle接口,LifeCycle接口用来统一管理各组件的生命周期。
Tomcat 启动流程
流程
启动tomcat , 需要调用 bin/startup.bat (在linux 目录下 , 需要调用 bin/startup.sh),在startup.bat 脚本中, 调用了catalina.bat。 在catalina.bat 脚本文件中,调用了BootStrap 中的main方法。 在BootStrap 的main 方法中调用了 init 方法 , 来创建Catalina 及 初始化类加载器。 在BootStrap 的main 方法中调用了 load 方法 , 在其中又调用了Catalina的load方法。 在Catalina 的load 方法中 , 需要进行一些初始化的工作, 并需要构造Digester 对象, 用于解析 XML。 然后在调用后续组件的初始化操作 。加载Tomcat的配置文件,初始化容器组件 ,监听对应的端口号, 准备接受客户端请求
源码解析
??由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性, 所以Tomcat在设计的时候, 基于生命周期管理抽象成了一个接口 Lifecycle ,而组件 Server、Service、Container、Executor、Connector 组件 , 都实现了一个生命周期的接口,从而具有了以下生命周期中的核心方法: ??init():初始化组件 ??start():开始组件 ??stop():停止组件 ??destroy():销毁组件
各组件实现
??上面我们提到的Server、Service、Engine、Host、Context都是接口, 下图中罗列了这些接口的默认实现类。当前对于 Endpoint组件来说,在Tomcat中没有对应的Endpoint接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类: NioEndpoint、Nio2Endpoint、AprEndpoint , 这三个实现类,分别对应于前面讲解链接器 Coyote时, 提到的链接器支持的三种IO模型:NIO,NIO2,APR , Tomcat8.5版本中,默认采用的是 NioEndpoint。
Tomcat的main方法位置
??org.apache.catalina.startup.BootStrap#main
总结
??从启动流程图中以及源码中,我们可以看出Tomcat的启动过程非常准化, 统一按照生命周期管理接口Lifecycle的定义进行启动。首先调init() 方法进行组件的逐级初始化操作,然后再调用start()方法进行启动。 每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法,组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。
Tomcat 请求处理流程
??Tomcat中维护了一个Mapper组件将用户请求的URL定位到一个Servlet,它的工作原理是:Mapper组件里保存了Web应用的配置信息,其实就是容器组件与访问路径的映射关系,比如Host容器里配置的域名、Context容器里的Web应用路径,以及Wrapper容器里Servlet映射的路径,这些配置信息就像是一个多层次的Map。当一个请求到来时,Mapper组件通过解析请求URL里的域名和路径,再到自己保存的Map里去查找,就能定位到一个Servlet。一个请求URL最后只会定位到一个Wrapper容器,也就是一个Servlet。下面的示意图中 , 就描述了 当用户请求链接 http://www.abcde.com/abc/def 之后, 是如何找到最终处理业务逻辑的servlet 。 ??上面这幅图只是描述了根据请求的URL如何查找到需要执行的Servlet , 下面从Tomcat的设计架构层面来分析Tomcat了请求处理的过程。 ??Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket。将连接交给线程池Executor处理,开始执行请求响应任务。 ??Processor组件读取消息报文,解析请求行、请求体、请求头,封装成Request对象。 ??CoyoteAdaptor组件负责将Connector组件和Engine容器关联起来,把生成的Request对象转换成ServletRequest,后面还会把ServletResponse转成HttpResponse对象返回回去。 ??Mapper组件根据请求行的URL值和请求头的Host值匹配由哪个Host容器、Context容器、Wrapper容器处理请求。 ??Engine容器的管道开始处理,管道中包含若干个Valve、每个Valve负责部分处理逻辑。执行完Valve后会执行基础的 Valve–StandardEngineValve,负责调用Host容器的Pipeline。 ??Host容器的管道开始处理,流程类似,最后执行 Context容器的Pipeline。 ??Context容器的管道开始处理,流程类似,最后执行 Wrapper容器的Pipeline。 ??Wrapper容器的管道开始处理,流程类似,最后执行 Wrapper容器对应的Servlet对象的处理方法。 ??Tomcat中的各个组件各司其职,组件之间松耦合,确保了整体架构的可伸缩性和可拓展性, 在Tomcat中,每个Container组件采用责任链模式来完成具体的请求处理。在Tomcat中定义了Pipeline 和 Valve 两个接口,Pipeline 用于构建责任链, 后者代表责任链上的每个处理器。Pipeline 中维护了一个基础的Valve,它始终位于Pipeline的末端(最后执行),封装了具体的请求处理和输出响应的过程。当然,我们也可以调用addValve()方法, 为Pipeline 添加其他的Valve, 后添加的Valve 位于基础的Valve之前,并按照添加顺序执行。Pipiline通过获得首个Valve来启动整合链条的执行.其实就是代码的同步执行。
Jasper
??JSP页面其实是通过JSP的引擎将JSP文件内容转成Java代码执行的,其实一个JSP页面就是一个Servlet。Jasper模块是Tomcat的JSP核心引擎,Tomcat使用Jasper对JSP语法进行解析,生成Servlet并生成Class字节码,用户在进行访问jsp时,会访问Servlet,最终将访问的结果直接响应在浏览器端 。另外,在运行的时候,Jasper还会检测JSP文件是否修改,如果修改,则会重新编译JSP文件。另外JSP页面是第一次访问这个Servlet时才会编译而不是启动Tomcat时就会编译,所以第一次访问时要慢一些。 目前大多采用前后端分离的形式很少企业还在直接使用JSP,只学用得到的,否则学了也是忘,所以这块不再赘述,有需要自行查询
Tomcat 服务器配置
??Tomcat 服务器的配置主要集中于 tomcat/conf 下的 catalina.policy、catalina.properties、context.xml、server.xml、tomcat-users.xml、web.xml 文件。server.xml是tomcat的核心配置,tomcat-user.xml主要是配置一些访问manager页面、域安全用户角色的信息,web.xml和webapp中的web.xml功能是一致的,控制一些访问Servlet的参数等等,但这儿是全局的,本次主要讲一下Server.xml
Server.xml
??server.xml 是tomcat 服务器的核心配置文件,包含了Tomcat的 Servlet 容器(Catalina)的所有配置。由于配置的属性特别多,我们在这里主要讲解其中的一部分重要配置。这是server.xml的框架
<Server>
<Listener />
<GlobaNamingResources>
</GlobaNamingResources>
<Service>
<Connector />
<Engine>
<Logger />
<Realm />
<host>
<Logger />
<Context />
</host>
</Engine>
</Service>
</Server>
??Server是server.xml的根元素,用于创建一个Server实例,默认使用的实现类是org.apache.catalina.core.StandardServer。
<Server port="8005" shutdown="SHUTDOWN">
...
</Server>
??port : Tomcat 监听的关闭服务器的端口。 ??shutdown: 关闭服务器的指令字符串。 ??Server内嵌的子元素为 Listener、GlobalNamingResources、Service。默认配置的5个Listener 的含义:
<!‐‐ 用于以日志形式输出服务器 、操作系统、JVM的版本信息 ‐‐>
<Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
<!‐‐ 用于加载(服务器启动) 和 销毁 (服务器停止) APR。 如果找不到APR库, 则会
输出日志, 并不影响Tomcat启动 ‐‐>
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!‐‐ 用于避免JRE内存泄漏问题 ‐‐>
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<!‐‐ 用户加载(服务器启动) 和 销毁(服务器停止) 全局命名服务 ‐‐>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<!‐‐ 用于在Context停止时重建Executor 池中的线程, 以避免ThreadLocal 相关的内存泄漏 ‐‐>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
??GlobalNamingResources 中定义了全局命名服务:
<GlobalNamingResources>
<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>
??Service元素用于创建 Service 实例,默认使用 org.apache.catalina.core.StandardService。默认情况下,Tomcat 仅指定了Service 的名称, 值为 “Catalina”。Service 可以内嵌的元素为 : Listener、Executor、Connector、Engine,其中 : Listener 用于为Service添加生命周期监听器, Executor 用于配置Service 共享线程池,Connector 用于配置Service 包含的链接器, Engine 用于配置Service中链接器对应的Servlet 容器引擎。一个Server服务器,可以包含多个Service服务。
<Service name="Catalina">
...
</Service>
??默认情况下,Service 并未添加共享线程池配置,如果不配置共享线程池,那么Catalina 各组件在用到线程池时会独立创建。 如果我们想添加一个共享的线程池, 可以在下添加如下配置:
<Executor name="tomcatThreadPool" 线程池名称,用于 Connector中指定。
namePrefix="catalina‐exec‐" 所创建的每个线程的名称前缀,一个单独的线程名称为 namePrefix+threadNumber。
maxThreads="200" 池中最大线程数。
minSpareThreads="100" 核心线程数
maxIdleTime="60000" 线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为6000(1分钟),单位毫秒。
maxQueueSize="Integer.MAX_VALUE" 在被执行前最大线程排队数目 需要根据硬件实际调整 如果太大可能内存溢出
prestartminSpareThreads="false" 启动线程池时是否启动 minSpareThreads部分线程。默认值为false,即不启动。
threadPriority="5" 线程池中线程优先级,默认值为5,值从1到10。
线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。
如果想使用自定义线程池首先需要实现org.apache.catalina.Executor接口。
className="org.apache.catalina.core.StandardThreadExecutor"/>
??Connector 用于创建链接器实例。默认情况下,server.xml 配置了两个链接器,一个支持HTTP协议,一个支持AJP协议。因此大多数情况下,我们并不需要新增链接器配置,只是根据需要对已有链接器进行优化。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
属性说明: ??port: 端口号,Connector 用于创建服务端Socket 并进行监听, 以等待客户端请求链接。如果该属性设置为0,Tomcat将会随机选择一个可用的端口号给当前Connector使用。 ??protocol : 当前Connector 支持的访问协议。 默认为 HTTP/1.1 , 并采用自动切换机制选择一个基于 JAVA NIO 的链接器或者基于本地APR的链接器(根据本地是否含有Tomcat的本地库判定)。 ??如果不希望采用上述自动切换的机制, 而是明确指定协议, 可以使用以下值。 Http协议:
org.apache.coyote.http11.Http11NioProtocol , 非阻塞式 Java NIO 链接器
org.apache.coyote.http11.Http11Nio2Protocol , 非阻塞式 JAVA NIO2 链接器
org.apache.coyote.http11.Http11AprProtocol , APR 链接器
AJP协议 :
org.apache.coyote.ajp.AjpNioProtocol , 非阻塞式 Java NIO 链接器
org.apache.coyote.ajp.AjpNio2Protocol ,非阻塞式 JAVA NIO2 链接器
org.apache.coyote.ajp.AjpAprProtocol , APR 链接器
??connectionTimeOut : Connector 接收链接后的等待超时时间, 单位为 毫秒。 -1 表示不超时。 ??redirectPort:当前Connector 不支持SSL(https)请求, 接收到了一个请求, 并且也符合 security-constraint 约束, 需要SSL传输,Catalina自动将请求重定向到指定的端口。 ??executor : 如果有使用共享线程池可以指定上面讲的共享线程池的名称 ??URIEncoding : 用于指定编码URI的字符编码, Tomcat8.x版本默认的编码为 UTF-8 ,Tomcat7.x版本默认为ISO-8859-1。 完整的协议:
<Connector port="8080"
protocol="HTTP/1.1"
executor="tomcatThreadPool"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
connectionTimeout="20000"
compression="on"
compressionMinSize="2048"
disableUploadTimeout="true"
redirectPort="8443"
URIEncoding="UTF‐8" />
??Engine 作为Servlet 引擎的顶级元素,内部可以嵌入: Cluster、Listener、Realm、Valve和Host。
<Engine name="Catalina" defaultHost="localhost">
...
</Engine>
属性说明: ??name: 用于指定Engine 的名称, 默认为Catalina 。该名称会影响一部分Tomcat的存储路径(如临时文件)。 ??defaultHost : 默认使用的虚拟主机名称, 当客户端请求指向的主机无效时, 将交由默认的虚拟主机处理, 默认为localhost。 ??Host 元素用于配置一个虚拟主机, 它支持以下嵌入元素:Alias、Cluster、Listener、Valve、Realm、Context。如果在Engine下配置Realm, 那么此配置将在当前Engine下的所有Host中共享。 同样,如果在Host中配置Realm , 则在当前Host下的所有Context 中共享。Context中的Realm优先级 > Host 的Realm优先级 > Engine中的Realm优先级。说的简单点就是Realm类似于Unix里面的group。在Unix中,一个group对应着系统的一组资源,某个group不能访问不属于它的资源。Tomcat用Realm来将不同的应用(类似系统资源)赋给不同的用户(类似group),没有权限的用户则不能访问相关的应用。用于配置安全管理角色,通常读取tomcat-uesrs.xml进行验证。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
...
</Host>
属性说明: ??name: 当前Host通用的网络名称, 必须与DNS服务器上的注册信息一致。 Engine中包含的Host必须存在一个名称与Engine的defaultHost设置一致。 ??appBase: 当前Host的应用基础目录, 当前Host上部署的Web应用均在该目录下(可以是绝对目录,相对路径)。默认为webapps。 ??unpackWARs: 设置为true, Host在启动时会将appBase目录下war包解压为目录。设置为false, Host将直接从war文件启动。 ??autoDeploy: 控制tomcat是否在运行时定期检测并自动部署新增或变更的web应用。 通过给Host添加别名,我们可以实现同一个Host拥有多个网络名称,然后就可以通过两个域名访问当前Host下的应用(需要确保DNS或hosts中添加了域名的映射配置)。
<Host name="www.web1.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Alias>www.web2.com</Alias>
</Host>
Context 用于配置一个Web应用,默认的配置如下:
<Context docBase="myApp" path="/myApp">
....
</Context>
属性描述: ??docBase:Web应用目录或者War包的部署路径。可以是绝对路径,也可以是相对于 Host appBase的相对路径。 ??path:Web应用的Context 路径。如果我们Host名为localhost, 则该web应用访问的根路径为: http://localhost:8080/myApp。 ??它支持的内嵌元素为:CookieProcessor, Loader, Manager,Realm,Resources,WatchedResource,JarScanner,Valve。
Tomcat-user.xml
??该配置文件中,主要配置的是Tomcat的用户,角色等信息,用来控制Tomcat中manager, host-manager的访问权限,还可以配合上面讲的Realm控制用户组的访问权限。Realm的相关内容可以参考这篇文章,写的已经很详细了。
Web 应用配置
??web.xml 是web应用的描述文件, 它支持的元素及属性来自于Servlet 规范定义 。 在Tomcat 中, Web 应用的描述信息包括 tomcat/conf/web.xml 中默认配置 以及 Web应用 WEB-INF/web.xml 下的定制配置。在tomcat中的web.xml进行的配置是对webappp下所有的应用都有影响。
ServletContext 初始化参数
??我们可以通过 添加ServletContext 初始化参数,它配置了一个键值对,这样我们可以在应用程序中使用 javax.servlet.ServletContext.getInitParameter()方法获取参数。
<context‐param>
<param‐name>contextConfigLocation</param‐name>
<param‐value>classpath:applicationContext‐*.xml</param‐value>
<description>Spring Config File Location</description>
</context‐param>
会话配置
??用于配置Web应用会话,包括 超时时间、Cookie配置以及会话追踪模式。它将覆盖server.xml 和 context.xml 中的配置。
<session‐config>
<session‐timeout>30</session‐timeout> 会话超时时间,单位 分钟
<cookie‐config> 用于配置会话追踪Cookie
<name>JESSIONID</name> Cookie的名称
<domain>www.itcast.cn</domain> Cookie的域名
<path>/</path> Cookie的路径
<comment>Session Cookie</comment> 注释
<http‐only>true</http‐only> cookie只能通过HTTP方式进行访问,JS无法读取或修改,此项可以增加网站访问的安全性。
<secure>false</secure> 此cookie只能通过HTTPS连接传递到服务器,而HTTP 连接则不会传递该信息。注意是从浏览器传递到服务器,服务器端的Cookie对象不受此项影响。
<max‐age>3600</max‐age> 以秒为单位表示cookie的生存期,默认为‐1表示是会话Cookie,浏览器关闭时就会消失。
</cookie‐config>
<tracking‐mode>COOKIE</tracking‐mode> 用于配置会话追踪模式,Servlet3.0版本中支持的追踪模式:COOKIE、URL、SSL
</session‐config>
Servlet 配置
??Servlet 的配置主要是两部分, servlet 和 servlet-mapping :
<servlet>
<servlet‐name>myServlet</servlet‐name> 指定servlet的名称, 该属性在web.xml中唯一。
<servlet‐class>cn.itcast.web.MyServlet</servlet‐class> 用于指定servlet类名
<init‐param> 用于指定servlet的初始化参数, 在应用中可以通HttpServlet.getInitParameter 获取。
<param‐name>fileName</param‐name>
<param‐value>init.conf</param‐value>
</init‐param>
<load‐on‐startup>1</load‐on‐startup> 用于控制在Web应用启动时,Servlet的加载顺序。 值小于0,web应用启动时,不加载该servlet, 第一次访问时加载。
<enabled>true</enabled> 若为false ,表示Servlet不处理任何请求。
</servlet>
<servlet‐mapping>
<servlet‐name>myServlet</servlet‐name>
<url‐pattern>*.do</url‐pattern>
<url‐pattern>/myservet/*</url‐pattern> 用于指定URL表达式,一个 servlet‐mapping可以同时配置多个 url‐pattern
</servlet‐mapping>
Servlet 中文件上传配置:
<servlet>
<servlet‐name>uploadServlet</servlet‐name>
<servlet‐class>cn.itcast.web.UploadServlet</servlet‐class>
<multipart‐config>
<location>C://path</location> 存放生成的文件地址。
<max‐file‐size>10485760</max‐file‐size> 允许上传的文件最大值。 默认值为‐1, 表示没有限制。
<max‐request‐size>10485760</max‐request‐size>针对该 multi/form‐data 请求的最大数量,默认值为‐1, 表示无限制。
<file‐size‐threshold>0</file‐size‐threshold> 当数量量大于该值时, 内容会被写入文件。
</multipart‐config>
</servlet>
Listener配置
??Listener用于监听servlet中的事件,例如context、request、session对象的创建、修改、删除,并触发响应事件。Listener是观察者模式的实现,在servlet中主要用于对context、request、session对象的生命周期进行监控。在servlet2.5规范中共定义了8中Listener。在启动时,ServletContextListener 的执行顺序与web.xml 中的配置顺序一致, 停止时执行顺序相反。
<listener>
<listener‐class>org.springframework.web.context.ContextLoaderListener</listener‐class>
</listener>
Filter配置
??filter 用于配置web应用过滤器, 用来过滤资源请求及响应。 经常用于认证、日志、加密、数据转换等操作, 配置如下:
<filter>
<filter‐name>myFilter</filter‐name> 用于指定过滤器名称,在web.xml中,过滤器名称必须唯一。
<filter‐class>cn.itcast.web.MyFilter</filter‐class> 过滤器的全限定类名, 该类必须实现Filter接口。
<async‐supported>true</async‐supported> 该过滤器是否支持异步
<init‐param> 用于配置Filter的初始化参数, 可以配置多个, 可以通过FilterConfig.getInitParameter获取
<param‐name>language</param‐name>
<param‐value>CN</param‐value>
</init‐param>
</filter>
<filter‐mapping>
<filter‐name>myFilter</filter‐name>
<url‐pattern>/*</url‐pattern> 指定该过滤器需要拦截的URL。
</filter‐mapping>
欢迎页面配置
??welcome-file-list 用于指定web应用的欢迎文件列表。尝试请求的顺序,从上到下。
<welcome‐file‐list>
<welcome‐file>index.html</welcome‐file>
<welcome‐file>index.htm</welcome‐file>
<welcome‐file>index.jsp</welcome‐file>
</welcome‐file‐list>
错误页面配置
??error-page 用于配置Web应用访问异常时定向到的页面,支持HTTP响应码和异常类两种形式。如果tomcat上面还有nginx,这个也可以在ng里面统一配置。
<error‐page>
<error‐code>404</error‐code>
<location>/404.html</location>
</error‐page>
<error‐page>
<error‐code>500</error‐code>
<location>/500.html</location>
</error‐page>
<error‐page>
<exception‐type>java.lang.Exception</exception‐type>
<location>/error.jsp</location>
</error‐page>
??PS:注意路径。 / 代表项目根路径。 ??例如 tomcat 默认有个 ROOT项目
JVM 配置
??最常见的JVM配置当属内存分配,因为在绝大多数情况下,JVM默认分配的内存可能不能够满足我们的需求,特别是在生产环境,此时需要手动修改Tomcat启动时的内存参数分配。
JVM配置选项
??windows 平台(catalina.bat):
set JAVA_OPTS=‐server ‐Xms2048m ‐Xmx2048m ‐XX:MetaspaceSize=256m ‐XX:MaxMetaspaceSize=256m ‐XX:SurvivorRatio=8
??linux 平台(catalina.sh):
JAVA_OPTS="‐server ‐Xms1024m ‐Xmx2048m ‐XX:MetaspaceSize=256m ‐XX:MaxMetaspaceSize=512m ‐XX:SurvivorRatio=8"
??参数说明: 配置之后, 重新启动Tomcat,访问Tomcat的管理页面可以看到最新的配置信息
Tomcat 管理配置
??从早期的Tomcat版本开始,就提供了Web版的管理控制台,他们是两个独立的Web应用,位于webapps目录下。Tomcat 提供的管理应用有用于管理的Host的host-manager和用于管理Web应用的manager。
host-manager
??Tomcat启动之后,可以通过 http://localhost:8080/host-manager/html访问该Web应用。 host-manager 默认添加了访问权限控制,当打开网址时,需要输入用户名和密码(conf/tomcat-users.xml中配置) 。所以要想访问该页面,需要在conf/tomcatusers.xml 中配置,并分配对应的角色: ????admin-gui:用于控制页面访问权限 ????admin-script:用于控制以简单文本的形式进行访问
<role rolename="admin‐gui"/>
<role rolename="admin‐script"/>
<user username="itcast" password="itcast" roles="admin‐script,admin‐gui"/>
manager
??manager的访问地址为 http://localhost:8080/manager, 同样, manager也添加了页面访问控制,因此我们需要为登录用户分配角色为:
<role rolename="manager‐gui"/>
<role rolename="manager‐script"/>
<user username="itcast" password="itcast" roles="admin‐script,admin‐gui,manager‐gui,manager‐script"/>
Tomcat 集群
??由于单台Tomcat的承载能力是有限的,当我们的业务系统用户量比较大,请求压力比较大时,单台Tomcat是扛不住的,这个时候,就需要搭建Tomcat的集群,而目前比较流程的做法就是通过Nginx来实现Tomcat集群的负载均衡。
部署Tomcat集群
准备 Tomcat
??在服务器上, 安装2台tomcat, 然后分别改Tomcat服务器的端口号 :
shutdown 8015 ‐‐‐‐‐‐‐‐‐> 8025
http 8888 ‐‐‐‐‐‐‐‐‐> 9999
ajp 8019 ‐‐‐‐‐‐‐‐‐> 8029
安装配置Nginx
??在当前服务器上 , 安装Nginx , 然后再配置Nginx, 配置nginx.conf :
upstream serverpool{
server localhost:8888;
server localhost:9999;
}
server {
listen 99;
server_name localhost;
location / {
proxy_pass http://serverpool/;
}
}
Session 共享方案
??在Tomcat集群中,如果应用需要用户进行登录,那么这个时候,用于tomcat做了负载均衡,则用户登录并访问应用系统时,就会出现问题 。 ??解决上述问题, 有以下几种方案:
ip_hash 策略
??一个用户发起的请求,只会请求到tomcat1上进行操作,另一个用户发起的请求只在tomcat2上进行操作 。那么这个时候,同一个用户发起的请求,都会通过nginx的ip_hash策略,将请求转发到其中的一台Tomcat上。
Session复制
??在servlet_demo01 工程中 , 制作session.jsp页面,分别将工程存放在两台 tomcat 的webapps/ 目录下:
<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
TOMCAT ‐ 9999 :
<br/>
sessionID : <%= session.getId()%>
<br/>
<%
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null && loginUser.toString().length()>0){
System.out.println("session 有值, loginUser = " + loginUser);
}else{
session.setAttribute("loginUser","ITCAST");
System.out.println("session 没有值");
}
%>
</body>
</html>
??通过nginx访问 , http://localhost:99/demo01/session.jsp ,访问到的两台Tomcat出现的sessionID是不一样的: ??上述现象,则说明两台Tomcat的Session各是各的,并没有进行同步,这在集群环境下是存在问题的。Session同步的配置如下: ????在Tomcat的conf/server.xml 配置如下:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
????在Tomcat部署的应用程序 servlet_demo01 的web.xml 中加入如下配置 :(两台都要加)
<distributable/>
??配置完毕之后, 再次重启两个 Tomcat服务。 ?? tomcat的session复制是基于IP组播(multicast)来完成的。将集群的tomcat通过配置相同的组播IP和端口来确定一个集群组, 当一个node的session发生变更的时候, 它会向集群组的所有节点发送变更的数据 (网络通讯方式,1,tcp/ip,一对一通讯。2,udp广播方式通讯,发送全部网内机器某一端口,需要的机器监听端口接受数据处理数据,不需要的机器直接不理睬就行。3,组播方式,配置统一的组播IP和端口来确定一个集群组,集群内开黑通讯) ???? tomcat cluser在jdk1.5及以上版本才被支持。 ????系统必须允许广播,Tomcat通过广播机制传递session复制信息。 启动报错: plain 严重: Unable to start cluster. org.apache.catalina.tribes.ChannelException: java.net.SocketException: 没有那个设备; No faulty members identified. 添加广播路由: plain [root@centosx64_tomcat1 ~]# route add -host 228.0.0.4 dev eth0(eth0为实际网卡名称) [root@centosx64_tomcat1 ~]# route -en Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 228.0.0.4 0.0.0.0 255.255.255.255 UH 0 0 0 eth2 192.168.70.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth2 ????检查Tomcat集群内的主机防火墙是否开启 解决报错: java.net.NoRouteToHostException: No route to host (Host unreachable) 如果防火墙打开,需要允许4000端口访问(tomcat cluster使用4000进行集群内通信): -A INPUT -m state --state NEW -m tcp -p tcp --dport 4000 -j ACCEPT ???? 确保java.net.InetAddress.getLocalHost().getHostAddress()返回值不为127.0.0.1 tomcat cluser 使用java.net.InetAddress.getLocalHost().getHostAddress()作为广播地址,所以要确保在主机上该方法返回值不能为127.0.0.1,否则无法正常组件集群。 具体来说,检查/etc/hosts文件内容,确保hostname不会被解析到127.0.0.1。 ????tomcat cluster使用广播消息的方式进行集群通信,所以一定要确保同一个集群内的主机都使用相同的广播端口号,默认值为:45564。 ?? 为了能正常使用tomcat cluser的session复制功能,需要在web应用web.xml中添加配置节点:<distributable/>。 ????Tomcat官方推荐只在小规模集群时使用。原因在于:tomcat cluster使用广播方式传递session消息,特别是使用org.apache.catalina.ha.session.DeltaManager时,会对集群内所有节点进行广播,这就限制了集群规模不能很大。否则,广播流量的负担会降低集群整体性能。另外,由于session数据都是存放在内存中,且每个节点都会保存集群中所有session对象的完整副本,随着集群规模的增大,会影响节点的性能。具体能够使用多大的集群规模,应该根据实际部署环境进行性能测试,当集群性能不会随着节点的增加而增大时,说明已经是可以部署的最大集群规模。但通常而言,对于tomcat cluster的集群规模,应该限制在个位数。
Tomcat 安全
配置安全
??删除webapps目录下的所有文件,禁用tomcat管理界面; ??注释或删除tomcat-users.xml文件内的所有用户权限; ??更改关闭tomcat指令或禁用。tomcat的server.xml中定义了可以直接关闭 Tomcat 实例的管理端口(默认8005)。可以通过 telnet 连接上该端口之后,输入 SHUTDOWN (此为默认关闭指令)即可关闭Tomcat 实例(注意,此时虽然实例关闭了,但是进程还是存在的)。由于默认关闭Tomcat 的端口和指令都很简单。默认端口为8005,指令为SHUTDOWN 。 ????方案一:
//更改端口号和指令:
<Server port="8456" shutdown="itcast_shut">
????方案二:此方案的缺点是无法使用shutdown.sh
//禁用8005端口:
<Server port="‐1" shutdown="SHUTDOWN">
应用安全
??在大部分的Web应用中,特别是一些后台应用系统,都会实现自己的安全管理模块(权限模块),用于控制应用系统的安全访问,基本包含两个部分:认证(登录/单点登录)和授权(功能权限、数据权限)两个部分。对于当前的业务系统,可以自己做一套适用于自己业务系统的权限模块,也有很多的应用系统直接使用一些功能完善的安全框架,将其集成到我们的web应用中,如:SpringSecurity、Apache Shiro等。
传输安全
Tomcat是支持HTTPS的,建议自行查询资料,这里记录内容不完整。若上层有Nginx一般在上层配置。
Tomcat性能优化部分
可以参考这篇文章已经写得很详细了
|