Tomcat(二):tomcat配置文件server.xml详解和部署简介
# 1. 入门示例:虚拟主机提供 web 服务
该示例通过设置虚拟主机来提供 web 服务,因为是入门示例,所以设置极其简单,只需修改$CATALINA_HOME/conf/server.xml
文件为如下内容即可,本文的 tomcat 安装在/usr/local/tomcat 下,因此$CATALINA_HOME=/usr/local/tomcat
。其中大部分都采用了默认设置,只是在 engine 容器中添加了两个 Host 容器。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<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 name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" enableLookups="false" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase" />
</Realm>
<!-- 从此处开始添加以下两个Host容器作为虚拟主机 -->
<!-- 定义一个在$CATALINA_HOME之外的虚拟主机 -->
<Host name="www.longshuai.com" appBase="/www/longshuai"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="/www/longshuai" reloadable="true" />
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="longshuai_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
<!-- 定义一个在$CATALINA_HOME/webapps下的虚拟主机 -->
<Host name="www.xiaofang.com" appBase="webapps/xiaofang"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="" reloadable="true" />
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="xiaofang_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
<!-- 默认虚拟主机localhost,可不修改 -->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
除了engine中定义的默认`localhost`虚拟主机,另外布置了两个虚拟主机`www.longshuai.com`和`www.xiaofang.com`,它们的程序目录分别为`/www/longshuai`和`$CATALINA_HOME/webapps/xiaofang`,所以需要提前建立好这两个目录。另外,在`context`中定义了`docBase`,对于uri路径/xuexi,它的文件系统路径分别为`/www/longshuai/xuexi`目录和`$CATALINA_HOME/webapps/xiaofang/xuexi`,所以也要在上面两个程序目录中定义好xuexi目录。除此之外,还分别为这3个虚拟主机定义了日志,它们的路径为相对路径logs,相对于`$CATALINA_HOME`。
再提供 appBase 目录和 docBase 目录。
mkdir -p /www/longshuai/xuexi
mkdir -p /usr/local/tomcat/webapps/xiaofang/xuexi
再提供测试用的 index.jsp 文件。内容大致如下,分别复制到以下四个目录中:
/www/longshuai/ /www/longshuai/xuexi/ /usr/local/tomcat/webapps/xiaofang/ /usr/local/tomcat/webapps/xiaofang/xuexi
并将 out.println 的输出内容分别稍作修改,使能够区分读取的是哪个 index.jsp。
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
<body>
<% out.println("hello world from longshuai Root"); %>
</body>
</html>
2
3
4
5
6
7
最后重启 catalina。
catalina.sh stop
catalina.sh start
2
再测试主机上添加 www.{longshuai,xiaofang}.com 的 host 记录。例如在 windows 上,在 C:\Windows\System32\drivers\etc\hosts 中添加如下记录:
192.168.100.22 www.longshuai.com www.xiaofang.com
在浏览器中进行测试,结果如下:
# 2. tomcat 体系结构基本说明
如下两图:上面的图是 tomcat 组件体系的简图,下面的图是 Service 组件细化后的图。
tomcat 高度模块化,各个模块之间有嵌套的父子关系。如果使用配置文件来描述,可以大致简化为如下:
<server>
<service>
<connector PORT />
<engine>
<host name=www.a.com appBase=/www/a >
<context path="" docBase=/www/a />
<context path="/xuexi" docBase=/www/a/xuexi />
</host>
<host>
<context />
</host>
</engine>
</service>
</server>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
其中:
server
组件是管理 tomcat 实例的组件,可以监听一个端口,从此端口上可以远程向该实例发送 shutdown 关闭命令。
service
组件是一个逻辑组件,用于绑定 connector 和 container,有了 service 表示可以向外提供服务,就像是一般的 daemon 类服务的 service。可以认为一个 service 就启动一个 JVM,更严格地说,一个 engine 组件才对应一个 JVM(定义负载均衡时,jvmRoute 就定义在 Engine 组件上用来标识这个 JVM),只不过 connector 也工作在 JVM 中。
connector
组件是 监听组件,它有四个作用:
- (1).开启监听套接字,监听外界请求,并和客户端建立 TCP 连接;
- (2).使用 protocolHandler 解析请求中的协议和端口等信息,如 http 协议、AJP 协议;
- (3).根据解析到的信息,使用 processer 将分析后的请求转发给绑定的 Engine;
- (4).接收响应数据并返回给客户端。
container
是容器,它是一类组件,在配置文件(如 server.xml)中没有体现出来。它包含 4 个容器类组件:engine 容器、host 容器、context 容器和 wrapper 容器。
engine
容器用于从 connector 组件处接收已建立的 TCP 连接,还用于接收客户端发送的 http 请求并分析请求,然后按照分析的结果将相关参数传递给匹配出的虚拟主机。engine 还用于指定默认的虚拟主机。
host
容器定义虚拟主机,由于 tomcat 主要是作为 servlet 容器的,所以为每个 webapp 指定了它们的根目录 appBase。
context
容器主要是根据 path 和 docBase 获取一些信息,将结果交给其内的 wrapper 组件进行处理(它提供 wrapper 运行的环境,所以它叫上下文 context)。一般来说,都采用默认的标准 wrapper 类,因此在 context 容器中几乎不会出现 wrapper 组件。
wrapper
容器对应 servlet 的处理过程。它开启 servlet 的生命周期,根据 context 给出的信息以及解析 web.xml 中的映射关系,负责装载相关的类,初始化 servlet 对象 init()、执行 servlet 代码 service()以及服务结束时 servlet 对象的销毁 destory()。
executor
组件为每个 Service 组件提供线程池,使得各个 connector 和 Engine 可以从线程池中获取线程处理请求(connector),从而实现 tomcat 的并发处理能力。一定要注意,Executor 的线程池大小是为 Engine 组件设置,而不是为 Connector 设置的,Connector 的线程数量由 Connector 组件的 acceptorThreadCount 属性来设置。如果要在配置文件中设置该组件,则必须设置在 Connector 组件的前面,以便在 Connector 组件中使用executor
属性来引用配置好的 Executor 组件。如果不显式设置,则采用 Connector 组件上的默认配置,默认配置如下:
- (1). maxThreads:最大线程数,默认值 200。
- (2). minSpareThreads:最小空闲线程数,默认值 25。
- (3). maxIdleTime:空闲线程的线程空闲多长时间才会销毁,默认值 60000 即 1 分钟。
- (4). prestartminSpareThreads:是否启动 executor 时就直接创建等于最小空闲线程数的线程,默认值为 false,即只在有连接请求进入时才会创建。
根据上面描述的 tomcat 组件体系结构,处理请求的大致过程其实很容易推导出来:
Client(request)-->Connector-->Engine-->Host-->Context-->Wrapper(response data)-->Connector(response header)-->Client
撇开 tomcat 作为 servlet 容器的行为。它和 apache、nginx 的功能大致都能对应上。例如以 nginx 为例,以下是 nginx 提供 web 服务时的配置结构:
server {
listen PORT;
server_name www.a.com; # 对应于<host name=www.a.com>
location / { # 对应于context path=""
root html; # 对应于docBase
}
location /xuexi { # 对应于context path="/xuexi"
root html/xuexi;
}
}
2
3
4
5
6
7
8
9
10
connetcor组件类似于nginx的listen指令。host容器类似于nginx的server指令,host容器中的name属性相当于nginx的server_name指令。engine组件则没有对应配置项,不过在nginx同样有engine的功能,例如默认的虚拟主机,分析URL来判断请求交给哪个虚拟主机处理等。context容器相当于location指令,context容器的path属性相当于location的uri匹配路径,docBase相当于location的中的root指令,即DocumentRoot。
tomcat作为简单的web服务程序大致如此,但它的核心毕竟是处理servlet和jsp,它必须得管理好每个webapp。因此,对于tomcat来说,必须要掌握部署webapp的方式。在tomcat上部署webapp时,必须要理解context的概念。<font color=red>对于tomcat而言,**每个context都应该算是一个webapp,其路径由docBase决定,该目录存放的是归档的war文件或未归档的webapp相关文件,而host容器中的appBase则是虚拟主机整理webapp的地方,一个appBase下可以有多个webapp,即多个context**。</font>
# 3. tomcat 的 appBase 和 docBase 详细说明
这两货虽然意义很明确,但"潜规则"很严重。以下面的配置为例。
<host name=www.a.com appBase=/www/a >
<context path="" docBase=/www/a />
<context path="/xuexi" docBase=/www/a/xuexi />
</host>
2
3
4
appBase 是虚拟主机存放 webapp 的目录,它可以是相对路径,也可以是绝对路径。如果是相对路径,则相对于**$CATALINA_HOME
**,严格并准确地说是$CATALINA_BASE。
path 是 URI 的匹配路径,相当于 nginx 的 location 后的路径。tomcat 要求每个虚拟主机必须配置一个空字符串的 path,该条 context 作为 URI 无法被明确匹配时的默认 context,它相当于 nginx 中
location / {}
的作用。docBase 则是每个 webapp 的存放目录(或者是已归档的 war 文件),它可以是相对路径,也可以是绝对路径,提供相对路径时它相对于 appBase。该目录一般在 appBase 的目录下,但并不规定一定要放在 appBase 下。对于 web 服务来说,它相当于 nginx 的 root 指令,但对于 webapp 来说,一个 context 就相当于一个 webapp,而 docBase 正是 webapp 的路径。
"潜规则"在于默认的 context 如何提供。有以下几种情况:
- 明确定义了
<context path="" docBase=webappPATH>
,此时默认 context 的处理路径为 webappPATH。 - 明确定义了
<context path="">
,但却没给定 docBase 属性,此时该默认 context 处理路径为 appBase/ROOT 目录,注意 ROOT 为大写。 - 完全没有定义
path=""
的 context 时,即 host 容器中没有明确的 path="",此时将隐式定义一个默认 context,处理路径为 appBase/ROOT 目录。 - 定义了 path 但没有定义 docBase 属性时,docBase 将根据 path 推断出它的路径。推断的规则如下:(注:此时推断的不是默认 context,而是对应 context 的 docbase)
context path context name 推断出的docBase路径
--------------------------------------------------
/foo /foo foo
/foo/bar /foo/bar foo/bar
Empty String Empty String ROOT
2
3
4
5
显然,没有给定 path=""或缺少 docbase 时,都以 ROOT 作为目录。以下是几个定义示例:
# 虚拟主机中没有定义任何context,将以appBase下的ROOT作为默认处理路径
<Host appBase="webapps">
</Host>
# 没有定义path=""的context,但定义了path非空的context,也将以ROOT作为默认处理路径
# 如果下面的Context容器中省略docBase属性,则推断出该context的docBase路径为appBase/xuexi
<Host appBase="webapps">
<Context path="/xuexi" docBase="webappPATH" />
</Host>
# 某个context定义了path="",该context将作为默认context
# 但该默认context如果没有定义docBase,将推断出其docBase路径为appBase/ROOT
<Host appBase="webapps">
<Context path="" docBase="webappPATH" />
</Host>
# 某个context定义了path="",该context将作为默认context
# 下面的默认context明确定义了docBase
<Host appBase="webapps">
<Context path="" docBase="webappPATH" />
</Host>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
举个直观的例子,如果某个 Host 配置如下。
<Host name="www.xiaofang.com" appBase="/www/xiaofang" unpackWARs="true" autoDeploy="true">
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
</Host>
2
3
那么浏览器访问http://www.xiaofang.com:8080/xuexi/
将请求/www/xiaofang/xuexi/index.jsp
。
由于没有定义 path=""的 Context 组件,因此浏览器访问http://www.xiaofang.com:8080
将请求/www/xiaofang/ROOT/index.jsp
。注意,是 ROOT 目录。
如果加上<Context path="" docBase="" reloadable="true" />
,则访问http://www.xiaofang.com:8080
将请求/www/xiaofang/index.jsp
。注意,不是 ROOT 目录,而是相对于 appBase 的根目录,即/www/xiaofang。
尽管本文解释了一大堆关于 appBase 和 docBase 的设置,但一般都会采用大众所熟知的配置方式:appBase 设置为"webapps",即$CATALINA_HOME/webapps
,而 docBase 设置为 webapps 下的 webapp 应用名。这样配置不仅符合 eclipse 部署 webapp 时默认的部署目录结构(eclipse 部署应用时,将 WebContent 下的内容复制到 docBase 下,将 servlet java 源代码编译后的 class 文件复制到 WEB-INF/classes 目录下),更利于维护 webapp 和相关配置。例如:
<Context docBase="MyWeb" path="/MyWeb" reloadable="true" />
<Context docBase="SecondWeb" path="/SecondWeb" reloadable="true" />
<Context docBase="WEB" path="/WEB" reloadable="true" />
2
3
但这样的配置有个缺点,因为项目名称一般都会带有大写字母,使得在浏览器访问时,也要带有大写字母。例如输入http://www.a.com/MyWeb/index.jsp
。因此,可采用另一种配置方式:设置 Host 的 appBase 为 webapps 下的某个目录,然后在 path 上配置 uri 匹配路径。如下:
<Host name="www.xiaofang.com" appBase="webapps/MyWeb" unpackWARs="true" autoDeploy="true">
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Context path="" docBase="" reloadable="true" />
</Host>
2
3
4
# 4. webapp 目录体系结构
webapp 有特定的组织格式,是一种层次型目录结构,通常包含了 servlet 代码文件、jsp 页面文件、类文件、部署描述符文件等等。
这些文件可能是以目录的形式存放,也可能会打包成各种归档格式的文件,如 jar、war 等。但 jsp 有规定,在 web 应用程序的根目录下,一般要有下面几个目录:
- /WEB-INF:此 webapp 的私有资源目录,从浏览器上是无法访问此目录资源的,通常 web.xml 放置于此目录
- /WEB-INF/classes:此 webapp 自有的类
- /WEB-INF/lib:此 webapp 自有能够打包为 jar 格式的类
- /META-INF:并非标准的 webapp 目录,有的应用程序才有。当该应用程序想独立定义自己的 context.xml 时可放入此目录,也是私有目录。
每个 webapp 要想被 tomcat 加载,一种方法是程序目录放在$catalina.home/webapps下,另一种方式是配置该webapp相关的context配置,使tomcat能找到此webapp。正如前文所说,webapp目录一般都会放在$catalina.home/webapps 下。
简单部署示例:
(1)对于 war 类归档程序:将归档文件复制到$CATALINA_BASE/webapps/目录中,并重启 tomcat 即可,tomcat 会自动展开 war 归档。例如官方提供了一个 sample.war 作为 tomcat 学习初级示例( https://tomcat.apache.org/tomcat-8.5-doc/appdev/sample/sample.war ),下载后只需将其放入 webapps 下即可。
(2)在测试 tomcat 的过程中,很多时候是未归档程序,这时可以手动创建目录来实现部署。需要创建的目录有 webapps/yourapp,此目录下还要创建 WEB-INF 目录,在 WEB-INF 目录中还要创建 classes 和 lib 目录。然后将 jsp 文件放在对应目录下即可,如写一个测试的 index.jsp 放在 yourapp 目录下。而对于已经开发完毕的 webapp,因为 idea 在打包发布测试 webapp 时已经设置好目录,因此只要将 webapp 的目录复制到 webapps 目录下即可。
# 5. tomcat 配置文件 server.xml 详解
tomcat配置文件中配置的是各个组件的属性,**全局**配置文件为`$CATALINA_HOME/conf/server.xml`,主要的组件有以下几项:Server,Service,Connector,Engine,Host,Alias,Context,Valve等。配置完配置文件后需要重启tomcat,但在启动后一定要检查tomcat是否启动成功,因为即使出错,很多时候它都不会报错,可从监听端口判断。
配置方法见官方手册 (opens new window),在页面的左边有各个组件的链接。
tomcat 的配置文件都是 xml 文件,以下是 xml 文件的常见规则:
- 文件第一行设置 xml 标识,表示该文件是 xml 格式的文件。例如
<?xml version="1.0" encoding="UTF-8"?>
。
- 文件第一行设置 xml 标识,表示该文件是 xml 格式的文件。例如
- xml 文件的注释方法为
<!-- XXX -->
,这可以是单行注释,也可以多行注释,只要前后注释符号能对应上,中间的内容都是注释。
- xml 文件的注释方法为
- 定义属性时有两种方式:单行定义和多行定义。例如:
<NAME key=value />
<!-- 多行定义的方式 -->
<NAME key=value>
</NAME>
2
3
4
5
下面个组件的配置中有些地方使用了相对于`$CATALINA_BASE`的相对路径,它和`$CATALINA_HOME`小有区别。如果只有一个tomcat实例,则它们是等价的,都是tomcat的安装路径。如果有多个tomcat实例,则`$CATALINA_HOME`表示的是安装路径,而`$CATALINA_BASE`表示的是各实例所在根目录。关于tomcat多实例,见[running.txt](http://tomcat.apache.org/tomcat-8.5-doc/RUNNING.txt)中对应的说明
# 5.1 顶级元素 server
server 组件定义的是一个 tomcat 实例。默认定义如下:
<Server port="8005" shutdown="SHUTDOWN">
</Server>
2
它默认监听在8005端口以接收shutdown命令。要启用多个tomcat实例,将它们监听在不同的端口即可。这个端口的定义为管理员提供一个关闭实例的便捷途径,可以直接telnet至此端口使用SHUTDOWN命令关闭此实例。不过基于安全角度的考虑,通常不允许远程进行。
Server 的相关属性:
className
:用于实现此组件的 java 类的名称,这个类必须实现接口 org.apache.catalina.Server。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardServer;address
:监听端口绑定的地址。如不指定,则默认为 Localhost,即只能在 localhost 上发送 SHUTDOWN 命令;port
:接收 shutdown 指令的端口,默认仅允许通过本机访问,默认为 8005;shutdown
:通过 TCP/IP 连接发往此 Server 用于实现关闭 tomcat 实例的命令字符串。
在 server 组件中可嵌套一个或多个 service 组件。
# 5.2 顶级元素 service
定义了 service 就能提供服务了。service 组件中封装 connector 和 container,它同时也表示将此 service 中的 connector 和 container 绑定起来,即由它们组成一个 service 向外提供服务。默认定义如下:
<Service name="Catalina">
</Service>
2
Service 相关的属性:
className
:用于实现 service 的类名,这个类必须实现 org.apache.catalina.Service 接口。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardService。name
:此 service 的显示名称,该名称主要用于在日志中进行标识 service。一般来说无关紧要,默认为 Catalina。
# 5.3 执行器 executor
执行器定义 tomcat 各组件之间共享的线程池。在以前,每个 connector 都会独自创建自己的线程池,但现在,可以定义一个线程池,各组件都可以共享该线程池,不过主要是为各 connector 之间提供共享。注意,executor 创建的是共享线程池,如果某个 connector 不引用 executor 创建的线程池,那么该 connector 仍会根据自己指定的属性创建它们自己的线程池。
连接器必须要实现org.apache.catalina.Executor接口。它是一个嵌套在service组件中的元素,为了挑选所使用的connector,该元素还必须定义在connector元素之前。
默认的定义如下:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
2
其中该组件的属性有:
className
:用于实现此组件的 java 类的名称,这个类必须实现接口 org.apache.catalina.Executor。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardThreadExecutor;name
:该线程池的名称,其他组件需要使用该名称引用该线程池。
标准类的属性包括:
threadPriority
:线程优先级,默认值为 5。daemon
:线程是否以 daemon 的方式运行,默认值为 true。namePrefix
:执行器创建每个线程时的名称前缀,最终线程的名称为:namePrefix+threadNumber。maxThreads
:线程池激活的最大线程数量。默认值为 200。minSpareThreads
:线程池中最少空闲的线程数量。默认值为 25。maxIdleTime
:在空闲线程关闭前的毫秒数。除非激活的线程数量小于或等于 minSpareThreads 的值,否则会有空闲线程的出现。默认值为 60000,即空闲线程需要保留 1 分钟的空闲时间才被杀掉。maxQueueSize
:可执行任务的最大队列数,达到队列上限时的连接请求将被拒绝。prestartminSpareThreads
:在启动 executor 时是否立即创建 minSpareThreads 个线程数,默认为 false,即在需要时才创建线程。
例如在 connector 中指定所使用的线程池,方式如下:
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
2
3
4
# 5.4 连接器 connector
连接器用于接收客户端发送的请求并返回响应给客户端。一个 service 中可以有多个 connector。有多种 connector,常见的为 http/1.1,http/2 和 ajp(apache jserv protocol)。在 tomcat 中,ajp 连接协议类型专用于 tomcat 前端是 apache 反向代理的情况下。
因此 tomcat 可以扮演两种角色:
- Tomcat 仅作为应用程序服务器:请求来自于前端的 web 服务器,这可能是 Apache, IIS, Nginx 等;
- Tomcat 既作为 web 服务器,也作为应用程序服务器:请求来自于浏览器。
Tomcat 应该考虑工作情形并为相应情形下的请求分别定义好需要的连接器才能正确接收来自于客户端的请求。
此处暂先介绍 HTTP/1.1 连接器的属性设置。ajp 后文再做介绍。
HTTP 连接器表示支持 HTTP/1.1 协议的组件。设置了该连接器就表示 catalina 启用它的独立 web 服务功能,当然,肯定也提供它必须的 servlets 和 jsp 执行功能。在一个 service 中可以配置一个或多个连接器,每个连接器都可以将请求转发给它们相关联的 engine 以处理请求、创建响应。
每个流入的请求都需要一个独立的线程来接收。当并发请求数量超出 maxThreads 指定的值时,多出的请求将被堆叠在套接字中,直到超出 acceptCount 指定的值。超出 accpetCount 的请求将以"connection refused"错误进行拒绝。
默认的定义如下:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
2
3
HTTP 连接器的属性实在太多,详细配置方法见官方手册 (opens new window)。通常定义 HTTP 连接器时必须定义的属性只有"port"。
address
:指定连接器监听的地址,默认为所有地址,即 0.0.0.0。maxThreads
:支持的最大并发连接数,默认为 200;如果引用了 executor 创建的共享线程池,则该属性被忽略。acceptCount
:设置等待队列的最大长度;通常在 tomcat 所有处理线程均处于繁忙状态时,新发来的请求将被放置于等待队列中;maxConnections
:允许建立的最大连接数。acceptCount 和 maxThreads 是接受连接的最大线程数。存在一种情况,maxConnections 小于 acceptCount 时,超出 maxConnections 的连接请求将被接收,但不会与之建立连接。port
:监听的端口,默认为 0,此时表示随机选一个端口,通常都应该显式指定监听端口。protocol
:连接器使用的协议,用于处理对应的请求。默认为 HTTP/1.1,此时它会自动在基于 Java NIO 或 APR/native 连接器之间进行切换。定义 AJP 协议时通常为 AJP/1.3。redirectPort
:如果某连接器支持的协议是 HTTP,当接收客户端发来的 HTTPS 请求时,则转发至此属性定义的端口。connectionTimeout
:等待客户端发送请求的超时时间,单位为毫秒,默认为 60000,即 1 分钟;注意,这时候连接已经建立。keepAliveTimeout
:长连接状态的超时时间。超出该值时,长连接将关闭。enableLookups
:是否通过 request.getRemoteHost()进行 DNS 查询以获取客户端的主机名;默认为 true,应设置为 false 防止反解客户端主机;compression
:是否压缩数据。默认为 off。设置为 on 时表示只压缩 text 文本,设置为 force 时表示压缩所有内容。应该在压缩和 sendfile 之间做个权衡。useSendfile
:该属性为 NIO 的属性,表示是否启用 sendfile 的功能。默认为 true,启用该属性将会禁止 compression 属性。
当协议指定为 HTTP/1.1 时,默认会自动在 NIO/APR 协议处理方式上进行按需切换。如要显式指定协议,方式如下:
<connector port="8080" protocol="HTTP/1.1">
<connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol">
<connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol">
<connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol">
2
3
4
其中 NIO 是 C/C++的非阻塞 IO 复用模型在 JAVA 中的 IO 实现,NIO2 即 AIO 是异步 NIO,即异步非阻塞 IO: NioProtocol :non blocking Java NIO connector Nio2Protocol:non blocking Java NIO2 connector AprProtocol :the APR/native connector 它们之间的异同点如下表所示:
Java Nio Connector | Java Nio2 Connector | APR/native Connector | |
---|---|---|---|
Classname | Http11NioProtocol | Http11Nio2Protocol | Http11AprProtocol |
Tomcat Version | 6.x onwards | 8.x onwards | 5.5.x onwards |
Support Polling | YES | YES | YES |
Polling Size | maxConnections | maxConnections | maxConnections |
Read Request Headers | Non Blocking | Non Blocking | Non Blocking |
Read Request Body | Blocking | Blocking | Blocking |
Write Response Headers and Body | Blocking | Blocking | Blocking |
Wait for next Request | Non Blocking | Non Blocking | Non Blocking |
SSL Support | Java SSL or OpenSSL | Java SSL or OpenSSL | OpenSSL |
SSL Handshake | Non blocking | Non blocking | Blocking |
Max Connections | maxConnections | maxConnections | maxConnections |
下面是一个定义了多个属性的 SSL 连接器:
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
2
3
4
# 5.5 容器类 engine
engine 是 service 组件中用来分析协议的引擎机器,它从一个或多个 connector 上接收请求,并将请求交给对应的虚拟主机进行处理,最后返回完整的响应数据给 connector,通过 connector 将响应数据返回给客户端。
只有一个 engine 元素必须嵌套在每个 service 中,且 engine 必须在其所需要关联的 connector 之后,这样在 engine 前面的 connector 都可以被此 engine 关联,而在 engine 后面的 connector 则被忽略,因为一个 service 中只允许有一个 engine。
定义方式大致如下:
<Engine name="Catalina" defaultHost="localhost">
</Engine>
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
</Engine>
2
3
4
5
常用的 engine 属性有:
className
:实现 engine 的类,该类必须实现 org.apache.catalina.Engine 接口。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardEngine。defaultHost
:指定处理请求的默认虚拟主机。在 Engine 中定义的多个虚拟主机的主机名称中至少有一个跟 defaultHost 定义的主机名称同名。name
:Engine 组件的名称,用于记录日志和错误信息,无关紧要的属性,可随意给定。jvmRoute
:在启用 session 粘性时指定使用哪种负载均衡的标识符。所有的 tomcat server 实例中该标识符必须唯一,它会追加在 session 标识符的尾部,因此能让前端代理总是将特定的 session 转发至同一个 tomcat 实例上。- 注意,jvmRoute 同样可以使用 jvmRoute 的系统属性来设置。如果此处设置了 jvmRoute,则覆盖 jvmRoute 系统属性。关于 jvmRoute 的使用,在后面 tomcat ajp 负载均衡的文章中介绍。
engine 是容器中的顶级子容器,其内可以嵌套一个或多个 Host 作为虚拟主机,且至少一个 host 要和 engine 中的默认虚拟主机名称对应。除了 host,还可以嵌套 releam 和 valve 组件。
# 5.6 容器类 host
host 容器用来定义虚拟主机。engine 从 connector 接收到请求进行分析后,会将相关的属性参数传递给对应的(筛选方式是从请求首部的 host 字段和虚拟主机名称进行匹配)虚拟 host 进行处理。如果没有合适的虚拟主机,则传递给默认虚拟主机。因此每个容器中必须至少定义一个虚拟主机,且必须有一个虚拟主机和 engine 容器中定义的默认虚拟主机名称相同。
大致定义方式如下:
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
2
3
常用属性说明:
className
:实现 host 容器的类,该类必须实现 org.apache.catalina.Host 接口。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardHost。name
:虚拟主机的主机名,忽略大小写(初始化时会自动转换为小写)。可以使用前缀星号通配符,如"*.a.com"。使用了星号前缀的虚拟主机的匹配优先级低于精确名称的虚拟主机。appBase
:此 Host 的 webapps 目录,即 webapp 部署在此虚拟主机上时的存放目录。包括非归档的 web 应用程序目录和归档后的 WAR 文件的目录。使用相对路径时基于$CATALINA_BASE。xmlBase
:部署在此虚拟主机上的 context xml 目录。startStopThreads
:启动 context 容器时的并行线程数。如果使用了自动部署功能,则再次部署或更新时使用相同的线程池。autoDeploy
:在 Tomcat 处于运行状态时放置于 appBase 目录中的应用程序文件是否自动进行 deploy 或自动更新部署状态。这等于同时开启了 deployOnStartup 属性和 reload/redeploy webapp 的功能。触发自动更新时将默认重载该 webapp。默认为 true。unpackWars
:在执行此 webapps 时是否先对归档格式的 WAR 文件解压再运行,设置为 false 时则直接执行 WAR 文件;默认为 true。设置为 false 时会损耗性能。workDir
:该虚拟主机的工作目录。每个 webapp 都有自己的临时 IO 目录,默认该工作目录为$CATALINA_BASE/work。
大多数时候都只需设置虚拟主机名称 name 和 appBase 属性即可,其余采用默认,默认时会自动部署 webapp。有时候还需要管理多个站点名称,即主机别名。可以使用 Alias 为 Host 指定的主机名定义主机别名。如:
<Host name="web.a.com" appBase="webapps" unpackWARs="true">
<Alias>www.a.com</Alias>
</Host>
2
3
自动部署指的是自动装载 webapp 以提供相关 webapp 的服务。
# 5.7 容器类 context
connector 和 container 是整个 tomcat 的心脏,而 context 则是 container 的心脏,更是 tomcat 心脏的心脏。它是真正管理 servlet 的地方,它的配置影响了 servlet 的工作方式。
一个 context 代表一个 webapp。servlet 中规定,每个 webapp 都必须基于已归档的 WAR(WEB application archive)文件或基于非归档相关内容所在目录。
catalina 基于对请求 URI 与 context 中定义的 path 进行最大匹配前缀的规则进行挑选,从中选出使用哪个 context 来处理该 HTTP 请求。这相当于 nginx 的 location 容器,catalina 的 path 就相当于 location 的 path,它们的作用是相同的。
每个 context 都必须在虚拟主机容器 host 中有一个唯一的 context name。context 的 path 不需要唯一,因为允许同一个 webapp 不同版本的共存部署。此外,必须要有一个 context 的 path 为 0 长度的字符串(如<Context path="" docBase="ROOT"/>
),该 context 是该虚拟主机的默认 webapp,用于处理所有无法被虚拟主机中所有 context path 匹配的请求(当然,不定义也可以,此时将自动隐式提供,见前文所述)。
关于 context name,它是从 context path 推断出来的,不仅如此,其余几个属性如 context basefile name 也是由此推断出来的。规则如下:
- 如果 path 不为空,则 context name 等于 context path,basefile name 取 path 中去除前缀"/"后的路径,且所有"/"替换为"#"。
- 如果 path 为空,则 context name 也为空,而 basefile 为 ROOT(注意是大写)。
例如:
context path context name basefile name deploy examples
-----------------------------------------------------------------
/foo /foo foo foo.xml,foo.war,foo
/foo/bar /foo/bar foo#bar foo#bar.xml,foo#bar.war,foo#bar
Empty String Empty String ROOT ROOT.xml,ROOT.war,ROOT
2
3
4
5
配置 context 时,强烈建议不要定义在 server.xml 中,因为定义在 conf/server.xml 中时,只能通过重启 tomcat 来重载生效,也就是说无法自动部署应用程序了。虽说官方如此推荐,但大多数人出于习惯和方便,还是会直接写在 server.xml 中,这并没有什么问题,无非是重启一下而已。
可以考虑定义在/META-INF/context.xml 中,如果此时设置了 copyXML 属性,在部署时会将此 context.xml 复制到$CATALINA_BASE/conf/enginename/hostname/下,并重命名为"basefile name.xml"。也可以直接定义在$CATALINA_BASE/conf/enginename/hostname/下的.xml 文件中,该路径的 xml 优先级高于/META-INF/context.xml。
还可以定义默认的 context.xml 文件,包括两种:
- (1)定义在$CATALINA_BASE/conf/context.xml 中,该默认 context 对所有 webapp 都生效;
- (2)定义在$CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default 中,该默认 context 只对该虚拟主机中的所有 webapp 生效。
定义方式大致如下:
<Host name="www.a.com" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT"/>
<Context path="/bbs" docBase="web/bbs" reloadable="true"/>
</Host>
2
3
4
5
其中第一个context的path为空字符串,表示它是默认的context。当浏览器中输入www.a.com时,由于无法匹配第二个context,所以被默认即第一个context处理,当浏览器中输入www.a.com/bbs时,将被第二个context处理,它将执行web/bbs所对应的webapp,并返回相关内容。
在 context 容器中可以定义非常多的属性,详细内容见官方手册 (opens new window),以下是常见的几个属性:
className
:实现 host 容器的类,该类必须实现 org.apache.catalina.Context 接口。不给定该属性时将采用默认的标准类 org.apache.catalina.core.StandardContext。cookies
:默认为 true,表示启用 cookie 来标识 session。docBase
:即 DocumentRoot,是该 webapp 的 context root,即归档 WAR 文件所在目录或非归档内容所在目录。可以是绝对路径,也可以是相对于该 webapp appBase 的相对路径。path
:定义 webapp path。注意,当 path=""时,表示默认的 context;另外只有在 server.xml 中才需要定义该属性,其他所有情况下都不能定义该属性,因为会根据 docBase 和 context 的 xml 文件名推断出 path。reloadable
:是否监控/WEB-INF/class 和/WEB-INF/lib 两个目录中文件的变化,变化时将自动重载。在测试环境下该属性很好,但在真实生产环境部署应用时不应该设置该属性,因为监控会大幅增加负载,因此该属性的默认值为 false。wrapperClass
:实现 wrapper 容器的类,wrapper 用于管理该 context 中的 servlet,该类必须实现 org.apache.catalina.Wrapper 接口,如果不指定该属性则采用默认的标准类。xmlNamespaceAware
:和 web.xml 的解析方式有关。默认为 true,设置为 false 可以提升性能。xmlValidation
:和 web.xml 的解析方式有关。默认为 true,设置为 false 可以提升性能。
# 5.8 被嵌套类 realm
realm 定义的是一个安全上下文,就像是以哪种方式存储认证时的用户和组相关的数据库。有多种方式可以实现数据存放:
- JAASRealm:基于 Java Authintication and Authorization Service 实现用户认证;
- JDBCRealm:通过 JDBC 访问某关系型数据库表实现用户认证;
- JNDIRealm:基于 JNDI 使用目录服务实现认证信息的获取;
- MemoryRealm:查找 tomcat-user.xml 文件实现用户信息的获取;
- UserDatabaseRealm:基于 UserDatabase 文件(通常是 tomcat-user.xml)实现用户认证,它实现是一个完全
- 可更新和持久有效的 MemoryRealm,因此能够跟标准的 MemoryRealm 兼容;它通过 JNDI 实现;
下面是一个常见的使用 UserDatabase 的配置:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
2
下面是一个使用 JDBC 方式获取用户认证信息的配置:
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/authority"
connectionName="test" connectionPassword="test"
userTable="users" userNameCol="user_name"
userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" />
2
3
4
5
6
7
# 5.9 被嵌套类 valve
Valve 中文意思是阀门,类似于过滤器,它可以工作于 Engine 和 Host/Context 之间、Host 和 Context 之间以及 Context 和 Web 应用程序的某资源之间。一个容器内可以建立多个 Valve,而且 Valve 定义的次序也决定了它们生效的次序。
有多种不同的 Valve:
- AccessLogValve:访问日志 Valve;
- ExtendedAccessValve:扩展功能的访问日志 Valve;
- JDBCAccessLogValve:通过 JDBC 将访问日志信息发送到数据库中;
- RequestDumperValve:请求转储 Valve;
- RemoteAddrValve:基于远程地址的访问控制;
- RemoteHostValve:基于远程主机名称的访问控制;
- SemaphoreValve:用于控制 Tomcat 主机上任何容器上的并发访问数量;
- JvmRouteBinderValve:在配置多个 Tomcat 为以 Apache 通过 mod_proxy 或 mod_jk 作为前端的集群架构中,当期望停止某节点时,可以通过此 Valve 将用记请求定向至备用节点;使用此 Valve,必须使用 JvmRouteSessionIDBinderListener;
- ReplicationValve:专用于 Tomcat 集群架构中,可以在某个请求的 session 信息发生更改时触发 session 数据在各节点间进行复制;
- SingleSignOn:将两个或多个需要对用户进行认证 webapp 在认证用户时连接在一起,即一次认证即可访问所有连接在一起的 webapp;
- ClusterSingleSingOn:对 SingleSignOn 的扩展,专用于 Tomcat 集群当中,需要结合 ClusterSingleSignOnListener 进行工作;
其中 RemoteHostValve 和 RemoteAddrValve 可以分别用来实现基于主机名称和基于 IP 地址的访问控制,控制本身可以通过 allow 或 deny 来进行定义,这有点类似于 Apache 的访问控制功能。如下面的 Valve 实现了仅允许本机访问/probe:
<Context privileged="true" path="/probe" docBase="probe">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1"/>
</Context>
2
3
4
其中相关属性定义有:
- className:在对应位置的后缀上加上".valves.RemoteHostValve"或".valves.RemoteAddrValve";
- allow:以逗号分开的允许访问的 IP 地址列表,支持正则,点号“.”用于 IP 地址时需要转义;仅定义 allow 项时,非明确 allow 的地址均被 deny;
- deny: 以逗号分开的禁止访问的 IP 地址列表,支持正则;使用方式同 allow;仅定义 deny 项时,非明确 deny 的地址均被 allow;
另外一个常用的 Valve 为 AccessLogValve,定义方式大致如下:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
2
3
其中 prefix 和 suffix 表示日志文件的前缀名称和后缀名称。pattern 表示记录日志时的信息和格式。
- 01
- idea 热部署插件 JRebel 安装及破解,不生效问题解决04-10
- 02
- spark中代码的执行位置(Driver or Executer)12-12
- 03
- 大数据技术之 SparkStreaming12-12