Tomcat学习笔记

转载:https://www.cnblogs.com/f-ck-need-u/p/8120008.html

1. 入门示例:虚拟主机提供web服务

该示例通过设置虚拟主机来提供web服务,因为是入门示例,所以设置极其简单,只需修改$CATALINA_HOME/conf/server.xml文件为如下内容即可,本文的tomcat安装在/usr/local/tomcat下,因此$CATALINA_HOME=/usr/local/tomcat。其中大部分都采用了默认设置,只是在engine容器中添加了两个Host容器。

1
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
<?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 &quot;%r&quot; %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 &quot;%r&quot; %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 &quot;%r&quot; %s %b" /> </Host> </Engine> </Service> </Server>

除了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目录。

1
2
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。

1
2
3
4
5
6
7
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
  <body>
    <% out.println("hello world from longshuai Root"); %>
  </body>
</html>

最后重启catalina。

1
2
catalina.sh stop
catalina.sh start

再测试主机上添加www.{longshuai,xiaofang}.com的host记录。例如在windows上,在C:\Windows\System32\drivers\etc\hosts中添加如下记录:

192.168.100.22 www.longshuai.com www.xiaofang.com

在浏览器中进行测试,结果如下:

Tomcat学习笔记

总结:定义两个站点,对访问路径进行分析

2. tomcat体系结构基本说明

如下两图:上面的图是tomcat组件体系的简图,下面的图是Service组件细化后的图。

Tomcat学习笔记

Tomcat学习笔记

tomcat高度模块化,各个模块之间有嵌套的父子关系。如果使用配置文件来描述,可以大致简化为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>

其中:

  1. server组件是管理tomcat实例的组件,可以监听一个端口,从此端口上可以远程向该实例发送shutdown关闭命令。
  2. service组件是一个逻辑组件,用于绑定connector和container,有了service表示可以向外提供服务,就像是一般的daemon类服务的service。可以认为一个service就启动一个JVM,更严格地说,一个engine组件才对应一个JVM(定义负载均衡时,jvmRoute就定义在Engine组件上用来标识这个JVM),只不过connector也工作在JVM中。
  3. connector组件是监听组件,它有四个作用:container是容器,它是一类组件,在配置文件(如server.xml)中没有体现出来。它包含4个容器类组件:engine容器、host容器、context容器和wrapper容器。
    • (1).开启监听套接字,监听外界请求,并和客户端建立TCP连接;
    • (2).使用protocolHandler解析请求中的协议和端口等信息,如http协议、AJP协议;
    • (3).根据解析到的信息,使用processer将分析后的请求转发给绑定的Engine;
    • (4).接收响应数据并返回给客户端。
  4. engine容器用于从connector组件处接收已建立的TCP连接,还用于接收客户端发送的http请求并分析请求,然后按照分析的结果将相关参数传递给匹配出的虚拟主机。engine还用于指定默认的虚拟主机。
  5. host容器定义虚拟主机,由于tomcat主要是作为servlet容器的,所以为每个webapp指定了它们的根目录appBase。
  6. context容器主要是根据path和docBase获取一些信息,将结果交给其内的wrapper组件进行处理(它提供wrapper运行的环境,所以它叫上下文context)。一般来说,都采用默认的标准wrapper类,因此在context容器中几乎不会出现wrapper组件。
  7. wrapper容器对应servlet的处理过程。它开启servlet的生命周期,根据context给出的信息以及解析web.xml中的映射关系,负责装载相关的类,初始化servlet对象init()、执行servlet代码service()以及服务结束时servlet对象的销毁destory()。
  8. executor组件为每个Service组件提供线程池,使得各个connector和Engine可以从线程池中获取线程处理请求,从而实现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服务时的配置结构:

1
2
3
4
5
6
7
8
9
10
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;
        }
}

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的概念。对于tomcat而言,每个context都应该算是一个webapp,其路径由docBase决定,该目录存放的是归档的war文件或未归档的webapp相关文件,而host容器中的appBase则是虚拟主机整理webapp的地方,一个appBase下可以有多个webapp,即多个context

3. tomcat的appBase和docBase详细说明

这两货虽然意义很明确,但"潜规则"很严重。以下面的配置为例。

1
2
3
4
<host name=www.a.com appBase=/www/a >                相当于Nginx中server块的主机名和root路径
    <context path="" docBase=/www/a />               相当于Nginx中定义的location路径
    <context path="/xuexi" docBase=/www/a/xuexi />   相当于Nginx中定义的二级location路径
</host>

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如何提供。有以下几种情况:

  1. 明确定义了<context path="" docBase=webappPATH>,此时默认context的处理路径为webappPATH。
  2. 明确定义了<context path="">,但却没给定docBase属性,此时该默认context处理路径为appBase/ROOT目录,注意ROOT为大写。
  3. 完全没有定义path=""的context时,即host容器中没有明确的path="",此时将隐式定义一个默认context,处理路径为appBase/ROOT目录。
  4. 定义了path但没有定义docBase属性时,docBase将根据path推断出它的路径。推断的规则如下:(注:此时推断的不是默认context,而是对应context的docbase)
1
2
3
4
5
context path    context name    推断出的docBase路径
--------------------------------------------------
/foo            /foo            foo    
/foo/bar        /foo/bar        foo/bar
Empty String    Empty String    ROOT

显然,没有给定path=""或缺少docbase时,都以ROOT作为目录。以下是几个定义示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 虚拟主机中没有定义任何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>

举个直观的例子,如果某个Host配置如下。

1
2
3
<Host name="www.xiaofang.com" appBase="/www/xiaofang" unpackWARs="true" autoDeploy="true">
    <Context path="/xuexi" docBase="xuexi" reloadable="true" />
</Host>

那么浏览器访问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。

总结:如果没有定义一级目录path,将访问默认路径ROOT目录,若果定义了一级目录,将使用appBase定义的路径

 

尽管本文解释了一大堆关于appBase和docBase的设置,但一般都会采用大众所熟知的配置方式:appBase设置为"webapps",即$CATALINA_HOME/webapps,而docBase设置为webapps下的webapp应用名。这样配置不仅符合eclipse部署webapp时默认的部署目录结构(eclipse部署应用时,将WebContent下的内容复制到docBase下,将servlet java源代码编译后的class文件复制到WEB-INF/classes目录下),更利于维护webapp和相关配置。例如:

1
2
3
<Context docBase="MyWeb" path="/MyWeb" reloadable="true" />
<Context docBase="SecondWeb" path="/SecondWeb" reloadable="true" />
<Context docBase="WEB" path="/WEB" reloadable="true" />

但这样的配置有个缺点,因为项目名称一般都会带有大写字母,使得在浏览器访问时,也要带有大写字母。例如输入http://www.a.com/MyWeb/index.jsp。因此,可采用另一种配置方式:设置Host的appBase为webapps下的某个目录,然后在path上配置uri匹配路径。如下:

1
2
3
4
<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>

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,因为eclipse在发布测试webapp时已经设置好目录,因此只要将webapp的目录复制到webapps目录下即可。

5. tomcat配置文件server.xml详解

tomcat配置文件中配置的是各个组件的属性,全局配置文件为$CATALINA_HOME/conf/server.xml,主要的组件有以下几项:Server,Service,Connector,Engine,Host,Alias,Context,Valve等。配置完配置文件后需要重启tomcat,但在启动后一定要检查tomcat是否启动成功,因为即使出错,很多时候它都不会报错,可从监听端口判断。

配置方法见官方手册,在页面的左边有各个组件的链接。

tomcat的配置文件都是xml文件,以下是xml文件的常见规则:

  1. 文件第一行设置xml标识,表示该文件是xml格式的文件。例如<?xml version="1.0" encoding="UTF-8"?>
  2. xml文件的注释方法为<!-- XXX -->,这可以是单行注释,也可以多行注释,只要前后注释符号能对应上,中间的内容都是注释。
  3. 定义属性时有两种方式:单行定义和多行定义。例如:
1
2
3
4
5
6
<!-- 单行定义的方式 -->
<NAME key=value />

<!-- 多行定义的方式 -->
<NAME key=value>
</NAME>

下面个组件的配置中有些地方使用了相对于$CATALINA_BASE的相对路径,它和$CATALINA_HOME小有区别。如果只有一个tomcat实例,则它们是等价的,都是tomcat的安装路径。如果有多个tomcat实例,则$CATALINA_HOME表示的是安装路径,而$CATALINA_BASE表示的是各实例所在根目录。关于tomcat多实例,见running.txt中对应的说明。

5.1 *元素server

server组件定义的是一个tomcat实例。默认定义如下:

1
2
<Server port="8005" shutdown="SHUTDOWN">
</Server>

它默认监听在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向外提供服务。默认定义如下:

1
2
<Service name="Catalina">
</Service>

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元素之前。

默认的定义如下:

1
2
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
    maxThreads="150" minSpareThreads="4"/>

其中该组件的属性有:

  • 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中指定所使用的线程池,方式如下:

1
2
3
4
<Connector executor="tomcatThreadPool"
           port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

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"错误进行拒绝。

默认的定义如下:

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

HTTP连接器的属性实在太多,详细配置方法见官方手册。通常定义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协议处理方式上进行按需切换。如要显式指定协议,方式如下:

1
2
3
4
<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">

其中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连接器:

1
2
3
4
<Connector port="8443"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" />

5.5 容器类engine

engine是service组件中用来分析协议的引擎机器,它从一个或多个connector上接收请求,并将请求交给对应的虚拟主机进行处理,最后返回完整的响应数据给connector,通过connector将响应数据返回给客户端。

只有一个engine元素必须嵌套在每个service中,且engine必须在其所需要关联的connector之后,这样在engine前面的connector都可以被此engine关联,而在engine后面的connector则被忽略,因为一个service中只允许有一个engine。

定义方式大致如下:

1
2
3
4
5
<Engine name="Catalina" defaultHost="localhost">
</Engine>

<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
</Engine>

常用的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容器中定义的默认虚拟主机名称相同。

大致定义方式如下:

1
2
3
<Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
</Host>

常用属性说明:

  • 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指定的主机名定义主机别名。如:

1
2
3
<Host name="web.a.com" appBase="webapps" unpackWARs="true">
  <Alias>www.a.com</Alias>
</Host>

自动部署指的是自动装载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

每个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(注意是大写)。

例如:

1
2
3
4
5
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

配置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生效。

定义方式大致如下:

1
2
3
4
5
<Host name="www.a.com"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
      <Context path="" docBase="ROOT"/>
      <Context path="/bbs" docBase="web/bbs" reloadable="true"/>
</Host>

其中第一个context的path为空字符串,表示它是默认的context。当浏览器中输入www.a.com时,由于无法匹配第二个context,所以被默认即第一个context处理,当浏览器中输入www.a.com/bbs时,将被第二个context处理,它将执行web/bbs所对应的webapp,并返回相关内容。

在context容器中可以定义非常多的属性,详细内容见官方手册,以下是常见的几个属性:

  • 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的配置:

1
2
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
  resourceName="UserDatabase"/>

下面是一个使用JDBC方式获取用户认证信息的配置:

1
2
3
4
5
6
7
<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" />

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:

1
2
3
4
<Context privileged="true" path="/probe" docBase="probe">
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
  allow="127\.0\.0\.1"/>
</Context>

其中相关属性定义有:

  • className:在对应位置的后缀上加上".valves.RemoteHostValve"或".valves.RemoteAddrValve";
  • allow:以逗号分开的允许访问的IP地址列表,支持正则,点号“.”用于IP地址时需要转义;仅定义allow项时,非明确allow的地址均被deny;
  • deny: 以逗号分开的禁止访问的IP地址列表,支持正则;使用方式同allow;仅定义deny项时,非明确deny的地址均被allow;

另外一个常用的Valve为AccessLogValve,定义方式大致如下:

1
2
3
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

其中prefix和suffix表示日志文件的前缀名称和后缀名称。pattern表示记录日志时的信息和格式。

总结:日志记录格式,访问控制

上一篇:IDEA创建web工程,不用Archetype(超简单)


下一篇:docker 实践五:端口映射和容器互联