Tomcat原理和配置详解
1 官方文档解读(持续更新)
官网https://tomcat.apache.org/tomcat-10.0-doc/
在开始之前,我们先安装一个Tomcat,这里安装的是apache-tomcat-10.0.23-windows-x64.zip
1.1 安装目录介绍
- /bin - Startup, shutdown, 以及其他一些脚本
- /conf - 配置文件和相关DTD. 其中最重要的是server.xml,它是容器的主要配置文件。
- /logs - 日志文件
- /webapps - web应用部署的地方
1.2 CATALINA_HOME和CATALINA_BASE
这是两个环境变量,我们可以配置也可以不配置。视情况而定。
CATALINA_HOME:Tomcat安装路径。包含了静态资源,比如.jar文件和二进制文件。
CATALINA_BASE:表示一个Tomcat实例的配置根路径。包含了配置文件、日志文件、部署的应用、以及一些其他运行时配置信息。对于一个CATALINA_BASE其最低配置是只包含conf/server.xml和conf/web.xml。
默认情况下,CATALINA_HOME和CATALINA_BASE是一个路径。
1.3 配置
<?xml version="1.0" encoding="utf-8" ?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <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" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="SpringMVC"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" /> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <!--HelloMule--> <!-- <Context path="/mule" docBase="/var/www/webapp/Mule" workDir="/var/www/webtemp/Mule" debug="5" reloadable="false" crossContext="true" /> --> </Host> <Host name="SpringMVC" appBase="/var/www/webapp/SpringMVC" unpackWARs="true" autoDeploy="false" xmlValidation="false" xmlNamespaceAware="false" /> </Engine> </Service> </Server>
Tomcat是多实例的,每个Tomcat实例的配置在conf/server.xml中。
官方文档查看方式:1、官网https://tomcat.apache.org/tomcat-10.0-doc/config/index.html,2、tomcat安装路径下的webapps\docs\config\index.html
1 Server组件
Tomcat应用由许多可配置的组件组成,其中Catalina组件是其他组件的顶层容器,对应server.xml中的<Server>元素,因此它必须是配置文件中最外层的元素,且是唯一的外层元素。
属性:
Attribute | Description |
---|---|
className |
实现了 |
address |
|
port |
等待shutdow命令的端口号. -1表示禁止执行shutdown命令. |
portOffset |
|
shutdown |
停止Tomcat实例的命令,由指定的端口监听和接收. |
utilityThreads |
当前Catalina实例中可用的线程数 |
其中port和shutdown是安装了tomcat后默认显式指定的。
子元素
<Service>
<GlobalNamingResources>
2 Service组件
<Service>元素表示Service组件,其是Catalina组件的子组件。Service组件表示一个或多个Connector组件的组合,这些组件共享一个Engine组件来处理传入请求。一个Server组件中可以有多个Service组件。
<Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="SpringMVC"> ... </Engine> </Service>
属性
Attribute | Description |
---|---|
className |
实现了 |
name |
Service的显示名, |
子元素
<Executor>,线程池。供Service中的其他组件使用,有一些默认值,比如最小线程活动线程数,最大线程数等
<Connector>,可以有多个
<Engine>,只允许有一个
3 Connector组件
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
上面列出的<Connector>标签表示一个支持HTTP/1.1协议的Connector组件。该组件使得Catalina除了具有执行servlets和jsp的功能之外,还具有了作为独立web服务器的功能。
该组件的一个特定实例监听某一端口的连接请求。在一个Service组件中可以有多个Connector组件,这些Connector组件和一个特定的Engine组件相关联,它们转发请求到这个Engine组件,处理请求和生成响应。
每个传入的非异步请求在请求期间都需要一个线程,<Connector>标签的minSpareThreads属性可以设置最小线程数(默认是10),当请并发求数超过minSpareThreads所配置的值时,将会创建新的线程来接收请求。
<Connector>标签的maxThreads属性可以设置最大请求处理线程数(默认是200),这个属性决定了这个Connector能够并发处理的请求数。当请求并发数超过maxThreads所配置的值时,新的连接将会被放到Connector创建的等待队列里面。
<Connector>标签的maxConnections属性可以设置最大连接数(默认是8192),当请求数超过这个配置时,Connector将不能缓存这些请求,操作系统将会缓存多余的请求。
<Connector>标签的acceptCount属性可以设置操作系统最大请求队列大小(默认是100),当请求数超过这个配置时,请求将会被丢弃。
4 Engine组件
<Engine>元素必须在<Service>元素里面,并且在所有<Connector>元素后面。
Engine组件接收和处理Connectors组件转发而来的请求,并把响应传递给相应的Connector。
Attribute | Description |
---|---|
className |
|
defaultHost |
Engine组件下可以有多个<Host>子组件,根据http请求中的域名的不同来对应不同的<Host>(虚拟主机)来处理请求。当http请求中的域名没有对应的<Host>时,defaultHost就起作用了。 |
name |
Engine组件的名称. |
<Engine>可以有一个或多个<Host>子元素,表示这个Server组件的多个虚拟主机,且有一个<Host>子元素和<Engine>的defaultHost属性相对应。
5 Host组件
<Host>元素表示一个虚拟主机,Host组件必须是Engine的子组件。
有时候为了节省主机资源,一个ip我们绑定多个域名,比如szj1.com和szj2.com。当我们访问这两个域名时,DNS服务器将会把域名解析成真正的ip,然后请求将会被转发到这个ip。然后我们在这个主机上部署了Tomcat,在tomcat中我们配置了两个Host组件,也就是两个虚拟主机
<Host name="szj1.com" appBase="/data/webapps/szj1" /> <Host name="szj2.com" appBase="/data/webapps/szj2" />
每一个虚拟主机对应一个appBase是我们部署的web应用程序。
这样在外界看来,他们访问的是不同的网站,其实是一个主机。
属性:
Attribute | Description |
---|---|
appBase |
web应用在虚拟主机上的基目录. 该目录下的web应用可能会被部署到该虚拟主机上. 可以是绝对路径,也可以是相对 |
xmlBase |
指包含该Host部署的应用程序的Context的xml文件路径。可以是绝对路径也可以是相对tomcat目录的相对路径。如果未指定将使用"conf//" |
autoDeploy |
指tomcat在运行时是否要定期检查有没有新的应用程序,如果true,tomcat会定期检查appBase 和xmlBase 目录,如果找到了会自动部署的web应用程序,默认为true |
deployOnStartup |
指tomcat在启动时是否应该自动部署来自该Host的web应用程序,默认为true |
6 Context组件
<Context path="/mule" docBase="/var/www/webapp/Mule" workDir="/var/www/webtemp/Mule" />
<Context>元素表示一个运行在虚拟主机中的web应用,这个web应用有两种形式:1、war包,2、和war包解压之后有相同结构的目录。具体目录结构可在Servlet规范中找到。
<Context>元素是<Host>的子元素。在一个<Host>可以定义任意多个<Context>元素。但是不建议直接在<Host>里面配置<Context>。有更合适的配置方式。见下面
Context的定义方式:
(1)直接在<Host>元素中定义,上面已经说过,不建议这么做。
(2)/META-INF/context.xml中配置。
(3)$CATALINA_BASE/conf/[enginename]/[hostname]/目录下(idea运行tomcat就是用的这种方式)
比较重要的属性:
path:url匹配,匹配到最合适的之后,会交由docBase路径指定的web应用中的映射文件找到合适的Servlet去处理。当没有匹配项时,默认交由path=""处理。
docBase:web应用的路径。
workDir:临时文件和目录的路径,如果未指定,默认为$CATALINA_BASE/work。
1.4 部署方式
在了解部署方式之前,我们先介绍一下上下文描述符:上下文描述符是一个简单的xml配置文件,其中包含了这个上下文的相关配置。
上下文描述符可以在如下路径下:
- $CATALINA_BASE/conf/[enginename]/[hostname]/[webappname].xml
- $CATALINA_BASE/webapps/[webappname]/META-INF/context.xml
文件1是以应用名称命名,文件2固定为context.xml
Tomcat启动时部署
1.5 idea部署tomcat的方式分析
Edit Configiation进行配置Tomcat服务器和部署web应用
下图是配置Server页面,我们选择本机上安装的Tomcat8.5.81
部署配置
我们可以看一下上图中选择的这个Artifacts
配置之后,我们点击运行。注意看一下打印的日志
E:\apache-tomcat-8.5.81\bin\catalina.bat run [2022-08-08 04:00:33,861] Artifact springmvc-demo:war exploded: Waiting for server connection to start artifact deployment... Using CATALINA_BASE: "C:\Users\xxx\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\06615320-a6e3-4b9e-b8fb-ca9e5c6083d2" Using CATALINA_HOME: "E:\apache-tomcat-8.5.81" Using CATALINA_TMPDIR: "E:\apache-tomcat-8.5.81\temp" Using JRE_HOME: "C:\Program Files\Java\jdk1.8.0_211\jre" Using CLASSPATH: "E:\apache-tomcat-8.5.81\bin\bootstrap.jar;E:\apache-tomcat-8.5.81\bin\tomcat-juli.jar" Using CATALINA_OPTS: "" Connected to the target VM, address: '127.0.0.1:49677', transport: 'socket'
我们可以看到CATALINA_BASE:C:\Users\xxx\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\06615320-a6e3-4b9e-b8fb-ca9e5c6083d2,这是Tomcat实例的路径
我们继续看conf/server.xml
<Host>的appBase为E:\apache-tomcat-8.5.81\webapps,表示web应用的位置,其中deployIgnore="^(?!(manager)|(tomee)$).*"表示忽略了的web应用,这里设置manager和tomee表示,除了这两个应用外,其他应用都不部署,具体部署哪个应用是在$CATALINA_BASE/conf/[enginename]/[hostname]/目录下配置的。下面会讲
<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" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" /> </Realm> <Host name="localhost" appBase="E:\apache-tomcat-8.5.81\webapps" unpackWARs="true" autoDeploy="true" deployOnStartup="false" deployIgnore="^(?!(manager)|(tomee)$).*"> <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>
$CATALINA_BASE/conf/Caltalina/localhost/springmvc_demo_war_exploded.xml
<Context path="/springmvc_demo_war_exploded" docBase="D:\code\springmvc-demo\target\springmvc-demo" />
这里配置了部署的应用的位置docBase。还配置了url中的路径path。
$CATALINA_BASE/conf/web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>classdebuginfo</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*.jsp</url-pattern> <url-pattern>*.jspx</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> ...省略
在web.xml文件中,配置了两个Servlet,名称分别是default和jsp,且这两个Servlet随Tomcat容器一起实例化。
我们知道servlet初始化时,会调用init(ServletConfig config)方法。在idea中调试的时候,我们会发现这个init方法调用了很多次,
下面列出了调试过程中打印的几个Servlet信息。
StandardEngine[Catalina].StandardHost[localhost].StandardContext[/springmvc_demo_war_exploded].StandardWrapper[default] StandardEngine[Catalina].StandardHost[localhost].StandardContext[/springmvc_demo_war_exploded].StandardWrapper[dispatcher] StandardEngine[Catalina].StandardHost[localhost].StandardContext[/springmvc_demo_war_exploded].StandardWrapper[jsp] StandardEngine[Catalina].StandardHost[localhost].StandardContext[/springmvc_demo_war_exploded].StandardWrapper[jsp] StandardEngine[Catalina].StandardHost[localhost].StandardContext[/manager].StandardWrapper[default] StandardEngine[Catalina].StandardHost[localhost].StandardContext[/manager].StandardWrapper[jsp]
下面分析其原因:
上面我们已经知道,idea调试时除了会部署当前web应用外,还会部署manager应用,根据server.xml中的配置,我们知道,这两个应用都部署在当前的Tomcat实例中,且都部署在这个Tomcat实例的Engine名为Catalina,虚拟主机名Host为localhost中。但是这个虚拟主机下部署了两个不同的web应用,分别是springmvc_demo_war_exploded和manager,每个应用都配置了随Tomcat启动的若干个servlet。我们可以对照下图理解
这些servlet初始化时都会调用init方法。这就是我们看到init被调用多次的原因。
总结:从这里,我们了解到,idea中启动一个tomcat实例,不会修改tomcat安装路径下的任何东西,真正做到了对tomcat安装路径零污染。