1. 入门示例:虚拟主机提供web服务
该示例通过设置虚拟主机来提供web服务,因为是入门示例,所以设置极其简单,只需修改$CATALINA_HOME/conf/server.xml文件为如下内容即可。其中大部分都采用了默认设置,只是在engine容器中添加了两个Host容器。
除了engine中定义的默认localhost虚拟主机,另外布置了两个虚拟主机www.longshuai.com和www.xiaofang.com,它们的程序目录分别为/www/longshuai和/www/xiaofang,所以需要提前建立好这两个目录。另外,在context中定义了docBase,对于uri路径/xuexi,它的文件系统路径为/www/{longshuai,xiaofang}/xuexi目录,所以也要在上面两个程序根目录中定义好xuexi目录。除此之外,还分别为这3个虚拟主机定义了日志,它们的路径为相对路径logs,相对于$CATALINA_HOME。
再提供appBase目录和docBase目录。
再提供测试用的index.jsp文件。内容大致如下,分别复制到/www/{longshuai,xiaofang}/和/www/{longshuai,xiaofang}/xuexi/下,并将out.println的输出内容分别稍作修改,使能够区分读取的是哪个index.jsp。
最后重启catalina。
再测试主机上添加www.{longshuai,xiaofang}.com的host记录。例如在windows上,在C:\Windows\System32\drivers\etc\hosts中添加如下记录:
在浏览器中进行测试,结果如下:
2. tomcat体系结构基本说明
如下图:
tomcat高度模块化,各个模块之间有嵌套的父子关系。如果使用配置文件来描述,可以大致简化为如下:
其中server组件是工作在后台管理tomcat实例的组件,可以监听一个端口,从此端口上可以远程向该实例发送shutdown关闭命令。
service组件是一个逻辑组件,绑定connector和containor,有了service表示可以向外提供服务,就像是一般的daemon类服务的service。
connector组件是服务监听组件,用于监听外界请求并建立TCP连接,然后将连接交给containor,之后可以从此连接传输数据,例如接收http请求,发送http响应等。
containor是容器,在配置文件中没有体现出来,它包含4个容器类组件:engine容器、host容器、context容器和wrapper容器。
engine容器用于从connector组件处接收已建立的TCP连接,还用于接收客户端发送的http请求并分析请求,然后按照分析的结果将相关参数传递给匹配出的虚拟主机。engine还用于指定默认的虚拟主机。
host容器定义虚拟主机,由于tomcat主要是作为servlet容器的,所以为每个web应用程序指定了它们的根目录appBase。
context容器对应servlet容器的处理过程。还可以指定相关的wrapper容器类,当然一般都采用默认的标准wrapper类。
最后当请求处理完毕后,context将响应数据返回给host,再返回给engine,再返回给connector,最后返回给客户端。
撇开tomcat作为servlet容器的行为。它和apache、nginx的功能大致都能对应上。例如以nginx为例,以下是nginx提供web服务时的配置结构:
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详细说明
这两货虽然意义很明确,但"潜规则"很严重。以下面的配置为例。
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的处理路径为webappPATH。
明确定义了,但却没给定docBase属性,此时该默认context处理路径为appBase/ROOT目录,注意ROOT为大写。
完全没有定义path=""的context时,即host容器中没有明确的path="",此时将隐式定义一个默认context,处理路径为appBase/ROOT目录。
定义了path但没有定义docBase属性时,docBase将根据path推断出它的路径。推断的规则如下:
以下是几个定义示例:
4. tomcat配置文件server.xml详解
tomcat配置文件中配置的是各个组件的属性,全局配置文件为$CATALINA_HOME/conf/server.xml,主要的组件有以下几项:Server,Service,Connector,Engine,Host,Alias,Context,Valve等。配置完配置文件后需要重启tomcat,但在启动后一定要检查tomcat是否启动成功,因为即使出错,很多时候它都不会报错,可从监听端口判断。
配置方法见官方手册,在页面的左边有各个组件的链接。
tomcat的配置文件都是xml文件,以下是xml文件的常见规则:
文件第一行设置xml标识,表示该文件是xml格式的文件。例如。
xml文件的注释方法为,这可以是单行注释,也可以多行注释,只要前后注释符号能对应上,中间的内容都是注释。
定义属性时有两种方式:单行定义和多行定义。例如:
下面个组件的配置中有些地方使用了相对于$CATALINA_BASE的相对路径,它和$CATALINA_HOME小有区别,如果只有一个tomcat实例,则它们是等价的,都是tomcat的安装路径。如果有多个tomcat实例,则$CATALINA_HOME表示的是安装路径,而$CATALINA_BASE表示的是各实例所在根目录。关于tomcat多实例,见running.txt中对应的说明。
4.1 顶级元素server
server组件定义的是一个tomcat实例。默认定义如下:
它默认监听在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组件。
4.2 顶级元素service
定义了service就能提供服务了。service组件中封装connector和containor,它同时也表示将此service中的connector和containor绑定起来,即由它们组成一个service向外提供服务。默认定义如下:
Service相关的属性:
className:用于实现service的类名,这个类必须实现org.apache.catalina.Service接口。不给定该属性时将采用默认的标准类org.apache.catalina.core.StandardService。
name:此service的显示名称,该名称主要用于在日志中进行标识service。一般来说无关紧要,默认为Catalina。
4.3 执行器executor
执行器定义tomcat各组件之间共享的线程池。在以前,每个connector都会独自创建自己的线程池,但现在,可以定义一个线程池,各组件都可以共享该线程池,不过主要是为各connector之间提供共享。注意,executor创建的是共享线程池,如果某个connector不引用executor创建的线程池,那么该connector仍会根据自己指定的属性创建它们自己的线程池。
连接器必须要实现org.apache.catalina.Executor接口。它是一个嵌套在service组件中的元素,为了挑选所使用的connector,该元素还必须定义在connector元素之前。
默认的定义如下:
其中该组件的属性有:
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中指定所使用的线程池,方式如下:
4.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以处理请求、创建响应。
如果想要配置某个web server的连接器,则使用AJP协议。
每个流入的请求都需要一个独立的线程来接收。当并发请求数量超出maxThreads指定的值时,多出的请求将被堆叠在套接字中,直到超出acceptCount指定的值。超出accpetCount的请求将以"connection refused"错误进行拒绝。
默认的定义如下:
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协议处理方式上进行按需切换,如要显式指定协议,方式如下:
其中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
它们之间的异同点如下表所示:
4.5 容器类engine
engine是service组件中用来分析协议的引擎机器,它从一个或多个connector上接收请求,并将请求交给对应的虚拟主机进行处理,最后返回完整的响应数据给connector,通过connector将响应数据返回给客户端。
只有一个engine元素必须嵌套在每个service中,且engine必须在其所需要关联的connector之后,这样在engine前面的connector都可以被此engine关联,而在engine后面的connector则被忽略,因为一个service中只允许有一个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组件。
4.6 容器类host
host容器用来定义虚拟主机。engine从connector接收到请求进行分析后,会将相关的属性参数传递给对应的(筛选方式是从请求首部的host字段和虚拟主机名称进行匹配)虚拟host进行处理。如果没有合适的虚拟主机,则传递给默认虚拟主机。因此每个容器中必须至少定义一个虚拟主机,且必须有一个虚拟主机和engine容器中定义的默认虚拟主机名称相同。
大致定义方式如下:
常用属性说明:
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和webBase属性即可,其余采用默认,默认时会自动部署webapp。有时候还需要管理多个站点名称,即主机别名。可以使用Alias为Host指定的主机名定义主机别名。如:
自动部署指的是自动装载webapp以提供相关webapp的服务。
4.7 容器类context
connector和containor是整个tomcat的心脏,而context则是containor的心脏,更是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是该虚拟主机的默认webapp,用于处理所有无法被虚拟主机中所有context path匹配的请求。
关于context name,它是从context path推断出来的,不仅如此,其余几个属性如context basefile name也是由此推断出来的。规则如下:
如果path不为空,则context name等于context path,basefile name取path中去除前缀"/"后的路径,且所有"/"替换为"#"。
如果path为空,则context name也为空,而basefile为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生效。
定义方式大致如下:
其中第一个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可以提升性能。
4.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的配置:
下面是一个使用JDBC方式获取用户认证信息的配置:
4.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:
其中相关属性定义有:
className:在对应位置的后缀上加上".valves.RemoteHostValve"或".valves.RemoteAddrValve";
allow:以逗号分开的允许访问的IP地址列表,支持正则,点号“.”用于IP地址时需要转义;仅定义allow项时,非明确allow的地址均被deny;
deny: 以逗号分开的禁止访问的IP地址列表,支持正则;使用方式同allow;仅定义deny项时,非明确deny的地址均被allow;
另外一个常用的Valve为AccessLogValve,定义方式大致如下:
其中prefix和suffix表示日志文件的前缀名称和后缀名称。pattern表示记录日志时的信息和格式。
想要朝这方面发展或者真心有兴趣的。可以找我要一些java的学习视频,Q号码:3300863615,这个是免费的,希望同学找我要的时候不要有理所应当的态度,毕竟都是我的心血,希望你是真的有一颗想要学好java的心,我也会尽所能的去帮助你成为一名优秀的程序员。
加我Q有以下几个要求:
1、面对目前流行的技术不知从何下手,需要突破技术瓶颈的。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽
拿高薪的。
3、如果没有工作经验,在校大学生,基础不行扎实,对java工作机制,常用设计
思想,常用java开发框架掌握熟练的。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在
技术领域继续突破的。
5. Q号:3300863615备注好信息!
6.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面学习
网友评论