基础
资源分配
静态资源
直接被游览器解析
动态资源
不能被游览器解析
通信三要素
协议,主机(IP),端口号
Tomcat下载安装
官网
安装
解压即可
目录结构
bin
可执行文件(bat为Windows的处理脚本,sh为Linux的处理脚本) startup.bat (开启Tomcat的脚本) shutdown.bat(关闭Tomcat的脚本)
conf 配置文件
配置文件 | 说明 |
logging-properties | 日志的配置文件 |
server.xml | 当前Tomcat的配置信息,如端口号等 |
tomcat-users.xml | tomcat的访问用户以及每个用户的信息 |
web.xml | tomcat部署的所有应用的配置文件 |
lib
tomcat依赖的jar包
logs
日志信息
temp
临时文件
webapps
tomcat默认应用部署的目录
work
java源码和class字节码存放的目录
Tomcat的启动与停止
配置环境
因为Tomcat中的运行环境需要在java环境下,所以先要有JAVA_HOME
在进行Tomcat的配置
配置CATALINA_HOME 值为Tomcat的安装路径
再将安装路径中的bin与lib目录配置到Path中
启动
双击startup.bat
在游览器搜索:http://localhost:8080/ 出现Tomcat的界面即可
关闭
直接关闭命令行,或者执行shutdown.bat
Tomcat源码
下载
官网中下载即可
运行
- 解压
- 进入解压目录,并创建一个目录,命名为home,将conf、webapps目录移动至home中
- 在当前目录下创建一个pom.xml文件,引入tomcat的依赖包
- ·
- ·
- ·
Tomcat架构
HTTP工作原理
规定服务器和客户端的通信格式
- 用户通过游览器向服务器发起请求
- 游览器发起TCP连接请求
- 服务器接收请求并建立连接
- 客户端生成HTML格式的数据包
- 客户端将生成的数据包发送至服务端
- 服务端解析HTTP格式的数据包
- 服务端执行请求
- 服务器生成HTTP格式的数据包
- 服务器发送响应的数据包给客户端
- 客户端游览器解析HTTP格式的数据包
- 游览器呈现HTML响应给客户
Tomcat整体架构
HTTP服务器请求处理(业务类实现Servlet接口)
连接器(coyote)
概念
供客户端访问的接外部口
负责
协议和IO操作
IO模型与协议
IO模型 | 描述 |
NIO | 非阻塞I/O,采用Java NIO类实现 |
NIO2 | 异步I/O,采用jdk 7最新的NIO2类库的实现 |
APR | 采用Apache可移植运行库实现,是C/C++编写的本地库,如果选择该方案,需要单独安装APR库 |
Tomcat支持的应用层协议
应用层协议 | 描述 |
HTTP/1.1 | 这是大部分Web应用采用的访问协议 |
AJP | 用于和Web服务器集成(如Apache),以实现对静态资源的优化以及集群部署 |
HTTP/2 | HTTP 2.0大幅度地提升了Web性能,下一代HTTP协议,自8.5以及9.0版本后支持 |
连接器的组件
容器(catalina)
概念
通过松耦合的方式集成coyote,以完成按照请求协议进行数据的读写等
地位
结构
Catalina 负责解析Tomcat的配置文件 , 以此来创建服务器Server组件,并根据命令来对其进行管理
Server 服务器表示整个Catalina Servlet容器以及其它组件,负责组装并启动Servlet引擎,Tomcat连接器,Server通过实现Lifecycle接口,提供了一种优雅的启动和关闭整个系统的方式
Service 服务是Server内部的组件,一个Server包含多个Service。它将若干个Connector组件绑定到一个Container(Engine)上
Connector 连接器,处理与客户端的通信,它负责接收客户请求,然后转给相关的容器处理,最后向客户返回响应结果
Container 容器,负责处理用户的servlet请求,并返回对象给web用户的模块
container结构(Tomcat设计了4种容器,分别是Engine、Host、Context和Wrapper。这4种容器不是平行关系,而是父子关系。, Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性)
Engine 表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine,但是一个引擎可包含多个Host
Host 代表一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context
Context 表示一个Web应用程序, 一个Web应用可包含多个Wrapper
Wrapper 表示一个Servlet,Wrapper 作为容器中的最底层,不能包含子容器
Tomcat的配置文件server.xml
Tomcat 采用了组件化的设计,它的构成组件都是可配置的,其中最外层的是Server,其他组件 按照一定的格式要求配置在这个顶层容器中
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context></Context>
</Host>
</Engine>
</Service>
</Server>
Tomcat是怎么管理这些容器的呢?你会发现这些容器具有父子关系,形成一个树形结构,你可能马上就想到了设计模式中的组合模式。没错,Tomcat就是用组合模式来管理这些容器的。
具体实现方法是,所有容器组件都实现了Container接口,因此组合模 式可以使得用户对单容器对象和组合容器对象的使用具有一致性。这里单容器对象指的是最底层的Wrapper,组合容器对象指的是上面的Context、Host或者Engine。
**下图尤其重要,Tomcat中Engine,Context,Wrapper,Host接口有很多实现,默认执行标准实现逻辑,即下图中实现类 **
Container接口扩展了LifeCycle接口,LifeCycle接口用来统一管理各组件的生命周期。
LifeCycle接口管理生命周期的主要方法
Tomcat启动流程
步骤(反射、模板等)
1、启动tomcat,需要调用 bin/startup.bat,在startup.bat 脚本中,调用了catalina.bat
2、在catalina.bat 脚本文件中,调用了BootStrap 中的main方法
3、在BootStrap 的main 方法中调用了init方法 , 来创建Catalina 及初始化类加载器
4、在BootStrap 的main 方法中调用了load 方法 , 在其中又调用了Catalina的load方法
5、在Catalina 的load方法中,需要进行一些初始化的工作,并需要构造Digester 对象,用于解析 XML
6、然后在调用后续组件的初始化操作
主要事物:加载Tomcat的配置文件,初始化容器组件,监听对应的端口号,准备接受客户端请求
源码解析
Lifecycle
由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性, 所以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
ProtocolHandler:Coyote协议接口,通过封装Endpoint和Processor , 实现针对具体协议的处理功能。Tomcat按照协议和IO提供了6个实现类
AJP协议
- AjpNioProtocol:采用NIO的IO模型
- AjpNio2Protocol:采用NIO2的IO模型
- AjpAprProtocol:采用APR的IO模型,需要依赖于APR库
HTTP协议
- Http11NioProtocol:采用NIO的IO模型,默认使用的协议(如果服务器没有安装APR)
- Http11Nio2Protocol:采用NIO2的IO模型
- Http11AprProtocol:采用APR的IO模型,需要依赖于APR库
请求处理流程
- 浏览器在请求一个Servlet时,会按照HTTP协议构造一个HTTP请求,通过Socket连接发送给Tomcat
- Tomcat通过不同的IO模型都可以接收到Socket的字节流数据
- 接收到数据后,按HTTP协议解析字节流,得到HttpServletRequest对象
- 再通过HttpServletRequest对象,也就是请求信息,找到该请求对应的Host、Context、Wrapper
- 然后将请求交给Engine层处理
- Engine层处理完,就会将请求交给Host层处理
- Host层处理完,就会将请求交给Context层处理
- Context层处理完,就会将请求交给Wrapper层处理
- Wrapper层在拿到一个请求后,就会生成一个请求所要访问的Servlet实例对象
- 调用Servlet实例对象的service()方法,并把HttpServletRequest对象当做入参
- 从而就调用到Servlet所定义的逻辑
Jasper组件
Jasper简介
本质是Servlet
编译方式(核心jar :jatl.jar standard.jar)
运行时编译
预编译
编译过程
Tomcat服务器配置
server.xml
核心配置文件
1、Server
Server元素在最顶层,代表整个Tomcat容器,因此它必须是server.xml中***一个最外层的元素。一个Server元素中可以有一个或多个Service元素
在***部分的例子中,在最外层有一个元素,shutdown属性表示关闭Server的指令;port属性表示Server接收shutdown指令的端口号,设为-1可以禁掉该端口。
Server的主要任务,就是提供一个接口让客户端能够访问到这个Service集合,同时维护它所包含的所有的Service的声明周期,包括如何初始化、如何结束服务、如何找到客户端要访问的Service。
2、Service
Service的作用,是在Connector和Engine外面包了一层,把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine;其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。
在***部分的例子中,Server中包含一个名称为“Catalina”的Service。实际上,Tomcat可以提供多个Service,不同的Service监听不同的端口,后文会有介绍
Executor(线程池配置)
属性 | 含义 |
className | Executor实现的完全限定的Java类名、默认值:org.apache.catalina.core.StandardThread-Executor |
daemon | 决定这一Executor的线程是否应该为后台线程。如果JVM中的所有其他非后台线程都结束了,则后台线程结束,要获得有关后台线程的详细解释,参见java.lang.Thread的Java 1.5(及更高版本)Javadoc网页、默认值:false |
name | 共享线程池的名字。这是Connector为了共享线程池要引用的名字。该名字必须唯一、默认值:None;需要的参数 |
namePrefix | 在JVM上,每个运行线程都可以有一个name 字符串。这一属性为线程池中每个线程的name字符串设置了一个前缀,Tomcat将把线程号追加到这一前缀的后面、默认值:tomcat-exec- |
maxIdleTime | 在Tomcat关闭一个空闲线程之前,允许空闲线程持续的时间(以毫秒为单位)。只有当前活跃的线程数大于minSpareThread的值,才会关闭空闲线程、默认值:60000(一分钟) |
maxThreads | 该线程池可以容纳的最大线程数、默认值:200 |
minSpareThreads | Tomcat应该始终打开的最小不活跃线程数、默认值:25 |
threadPriority | 整数值,表示线程池中所有线程的线程优先权 |
3、Connector
Connector的主要功能,是接收连接请求,创建Request和Response对象用于和请求端交换数据;然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine。
通过配置Connector,可以控制请求Service的协议及端口号。在部分的例子中,Service包含两个Connector:
(1)通过配置第1个Connector,客户端可以通过8080端口号使用http协议访问Tomcat。其中,protocol属性规定了请求的协议,port规定了请求的端口号,redirectPort表示当强制要求https而请求是http时,重定向至端口号为8443的Connector,connectionTimeout表示连接的超时时间
在这个例子中,Tomcat监听HTTP请求,使用的是8080端口,而不是正式的80端口;实际上,在正式的生产环境中,Tomcat也常常监听8080端口,而不是80端口。这是因为在生产环境中,很少将Tomcat直接对外开放接收请求,而是在Tomcat和客户端之间加一层代理服务器(如nginx),用于请求的转发、负载均衡、处理静态文件等;通过代理服务器访问Tomcat时,是在局域网中,因此一般仍使用8080端口
(2)通过配置第2个Connector,客户端可以通过8009端口号使用AJP协议访问Tomcat。AJP协议负责和其他的HTTP服务器(如Apache)建立连接;在把Tomcat与其他HTTP服务器集成时,就需要用到这个连接器。之所以使用Tomcat和其他服务器集成,是因为Tomcat可以用作Servlet/JSP容器,但是对静态资源的处理速度较慢,不如Apache和IIS等HTTP服务器;因此常常将Tomcat与Apache等集成,前者作Servlet容器,后者处理静态资源,而AJP协议便负责Tomcat和Apache的连接。Tomcat与Apache等集成的原理如下图(图片来源):
4、Engine
Engine组件在Service组件中有且只有一个;Engine是Service组件中的请求处理组件。Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector,最终传递给客户端。
前面已经提到过,Engine、Host和Context都是容器,但它们不是平行的关系,而是父子关系:Engine包含Host,Host包含Context。
在***部分的例子中,Engine的配置语句如下:
其中,name属性用于日志和错误信息,在整个Server中应该***。defaultHost属性指定了默认的host名称,当发往本机的请求指定的host名称不存在时,一律使用defaultHost指定的host进行处理;因此,defaultHost的值,必须与Engine中的一个Host组件的name属性值匹配。
5、Host
(1)Engine与Host
Host是Engine的子容器。Engine组件中可以内嵌1个或多个Host组件,每个Host组件代表Engine中的一个虚拟主机。Host组件至少有一个,且其中一个的name必须与Engine组件的defaultHost属性相匹配
(2)Host的作用
Host虚拟主机的作用,是运行多个Web应用(一个Context代表一个Web应用),并负责安装、展开、启动和结束每个Web应用。
Host组件代表的虚拟主机,对应了服务器中一个网络名实体(如”www.test.com”,或IP地址”116.25.25.25”);为了使用户可以通过网络名连接Tomcat服务器,这个名字应该在DNS服务器上注册。
客户端通常使用主机名来标识它们希望连接的服务器;该主机名也会包含在HTTP请求头中。Tomcat从HTTP头中提取出主机名,寻找名称匹配的主机。如果没有匹配,请求将发送至默认主机。因此默认主机不需要是在DNS服务器中注册的网络名,因为任何与所有Host名称不匹配的请求,都会路由至默认主机。
(3)Host的配置
在***部分的例子中,Host的配置如下:
下面对其中配置的属性进行说明:
name属性指定虚拟主机的主机名,一个Engine中有且仅有一个Host组件的name属性与Engine组件的defaultHost属性相匹配;一般情况下,主机名需要是在DNS服务器中注册的网络名,但是Engine指定的defaultHost不需要,原因在前面已经说明。
unpackWARs指定了是否将代表Web应用的WAR文件解压;如果为true,通过解压后的文件结构运行该Web应用,如果为false,直接使用WAR文件运行Web应用。
Host的autoDeploy和appBase属性,与Host内Web应用的自动部署有关;此外,本例中没有出现的xmlBase和deployOnStartup属性,也与Web应用的自动部署有关;将在下一节(Context)中介绍。
6、Context
(1)Context的作用
Context元素代表在特定虚拟主机上运行的一个Web应用。在后文中,提到Context、应用或Web应用,它们指代的都是Web应用。每个Web应用基于WAR文件,或WAR文件解压后对应的目录(这里称为应用目录)。
Context是Host的子容器,每个Host中可以定义任意多的Context元素。
在***部分的例子中,可以看到server.xml配置文件中并没有出现Context元素的配置。这是因为,Tomcat开启了自动部署,Web应用没有在server.xml中配置静态部署,而是由Tomcat通过特定的规则自动部署。下面介绍一下Tomcat自动部署Web应用的机制。
(2)Web应用自动部署
Host的配置
要开启Web应用的自动部署,需要配置所在的虚拟主机;配置的方式就是前面提到的Host元素的deployOnStartup和autoDeploy属性。如果deployOnStartup和autoDeploy设置为true,则tomcat启动自动部署:当检测到新的Web应用或Web应用的更新时,会触发应用的部署(或重新部署)。二者的主要区别在于,deployOnStartup为true时,Tomcat在启动时检查Web应用,且检测到的所有Web应用视作新应用;autoDeploy为true时,Tomcat在运行时定期检查新的Web应用或Web应用的更新。除此之外,二者的处理相似
通过配置deployOnStartup和autoDeploy可以开启虚拟主机自动部署Web应用;实际上,自动部署依赖于检查是否有新的或更改过的Web应用,而Host元素的appBase和xmlBase设置了检查Web应用更新的目录
其中,appBase属性指定Web应用所在的目录,默认值是webapps,这是一个相对路径,代表Tomcat根目录下webapps文件夹
xmlBase属性指定Web应用的XML配置文件所在的目录,默认值为conf//,例如部分的例子中,主机localhost的xmlBase的默认值是$TOMCAT_HOME/conf/Catalina/localhost
tomcat-users.xml
Web应用配置
在web.xml中配置
工程目录下配置web.xml
Tomcat管理配置
在安装目录下的webapps中的host-manager 和 manager目录下配置
Tomcat中JVM的配置
JVM内存
-Xmx512m | 最大总堆内存,一般设置为物理内存的1/4 |
-Xms512m | 初始总堆内存,一般将它设置的和最大堆内存一样大,这样就不需要根据当前堆使用情况而调整堆的大小了 |
-Xmn192m | 年轻带堆内存,sun官方推荐为整个堆的3/8 |
堆内存的组成 | 总堆内存 = 年轻带堆内存 + 年老带堆内存 + 持久带堆内存 |
年轻带堆内存 | 对象刚创建出来时放在这里 |
年老带堆内存 | 对象在被真正会回收之前会先放在这里 |
持久带堆内存 | class文件,元数据等放在这里 |
-XX:PermSize=128m | 持久带堆的初始大小 |
-XX:MaxPermSize=128m | 持久带堆的最大大小,eclipse默认为256m。如果要编译jdk这种,一定要把这个设的很大,因为它的类太多了。 |
操作脚本(catalina.bat)来配置选项(Linux为catalina.sh)
属性 | 含义 |
-Xms | JVM初始化内存大小,默认是物理内存的1/64 |
-Xmx | JVM最大内存大小,默认是物理内存的1/4。(一般都把这2个值设置成一样的,防止每次GC后改变堆的大小) |
-Xmn | 新生代的内存大小,建议为整个堆内存的3/8 |
-XX:MetaspaceSize | 元空间内存初始大小 |
-XX:MaxMetaspaceSize | 元空间最大内存大小 |
-XX:permSize | 非堆内存大小(方法区),默认是物理内存的1/64 |
-XX:MaxPermSize | 非堆内存最大大小(方法区),默认是物理内存的1/4 |
-XX:NewSize | 新生代大小 |
-XX:MaxNewSize | 新生代最大大小 |
-XX:NewRatio | 新生代和老生带的内存配比 |
-XX:SurvivorRatio | Eden与From和To的内存配比 |
Tomcat集群
原因
一台Tomcat不能满足需求
使用原理
步骤
- 复制两个Tomcat,并改端口号
- 下载Nginx
- 安装配置Nginx
upstream serverpool {
server localhost:8888;
server localhost:9999;
}
server {
listen 99;
server_name localhost;
location / {
proxy_pass http://serverpool/;
}
}
负载均衡策略
轮询(可以通过weight权重改动访问概率)
ip-hash:可以通过客户端ip计算hash值,通过hash值决定服务端