Tomcat
安装Tomcat
- JDK https://www.oracle.com/java/technologies/downloads/archive/
- OpenJDK https://jdk.java.net/21/ 或 https://learn.microsoft.com/zh-cn/java/openjdk/download
- Tomcat https://tomcat.apache.org/
- 注意:
Tomcat/conf/startup.sh
配置文件错误时不会报错。Tomcat/conf/shutdown.sh
重复关闭时会报错。
// 安装Tomcat
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
]# ls apache-tomcat-8.0.30.tar.gz
apache-tomcat-8.0.30.tar.gz
]# yum install -y java-1.8.0-openjdk-devel // Tomcat使用Java开发,依赖包含java-1.8.0-openjdk
]# tar -xf apache-tomcat-8.0.30.tar.gz
]# mv apache-tomcat-8.0.30 /usr/local/tomcat // 解压即用
]# cd /usr/local/tomcat
tomcat]# bin/startup.sh // 配置文件错误时不会报错,需手动检验是否成功开启
tomcat]# ss -ntulp | grep java
tcp LISTEN 0 100 *:8080 *:* users:(("java",pid=1424,fd=48)) // HTTP端口
tcp LISTEN 0 1 [::ffff:127.0.0.1]:8005 *:* users:(("java",pid=1424,fd=78)) // 关闭服务端口
tcp LISTEN 0 100 *:8009 *:* users:(("java",pid=1424,fd=53)) // AJP端口
// Tomcat启动需要从/dev/random读取大量的随机数据,若启动速度较慢,可以创建软连接`ln -s /dev/urandom /dev/random`,否则可能由于/dev/random随机数据不足而导致的虽然监听端口但无法正常提供服务
tomcat]# mv /dev/random{,.ori}
tomcat]# ln -s /dev/urandom /dev/random
tomcat]# bin/shutdown.sh
tomcat]# bin/startup.sh
tomcat]# ls
bin conf lib LICENSE logs NOTICE RELEASE-NOTES RUNNING.txt temp webapps work
tomcat]# bin/version.sh
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/8.0.30
Server built: Dec 1 2015 22:30:46 UTC
Server number: 8.0.30.0
OS Name: Linux
OS Version: 4.18.0-372.9.1.el8.x86_64
Architecture: amd64
JVM Version: 1.8.0_332-b09
JVM Vendor: Red Hat, Inc.
tomcat]# java -version
openjdk version "1.8.0_332"
OpenJDK Runtime Environment (build 1.8.0_332-b09)
OpenJDK 64-Bit Server VM (build 25.332-b09, mixed mode)
tomcat]# cp conf/server.xml{,.default}
systemd.service
- https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html
// 创建tomcat.service
// 情况一:YUM安装jdk或openjdk
[root@tomcat ~]# which java
/usr/bin/java
[root@tomcat ~]# cat /usr/lib/systemd/system/tomcat.service
#/usr/lib/systemd/system/tomcat.service
[Unit]
Description=Tomcat Service
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
ExecReload=/usr/local/tomcat/bin/startup.sh && sleep 2 && /usr/local/tomcat/bin/shutdown.sh
[Install]
WantedBy=multi-user.target
[root@tomcat ~]# systemctl daemon-reload
// 情况二:自定义安装jdk或openjdk
[root@tomcat ~]# tar -xf jdk-8u361-linux-x64.tar.gz
[root@tomcat ~]# ln -s /root/jdk1.8.0_361/ /usr/local/jdk
[root@tomcat ~]# cat /etc/sysconfig/tomcat
JAVA_HOME=/usr/local/jdk
PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:${JAVA_HOME}/bin:${JAVA_HOME}/jre/bin
CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
[root@tomcat ~]# cat /usr/lib/systemd/system/tomcat.service
#/usr/lib/systemd/system/tomcat.service
[Unit]
Description=Tomcat Service
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/tomcat
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
ExecReload=/usr/local/tomcat/bin/startup.sh && sleep 2 && /usr/local/tomcat/bin/shutdown.sh
[Install]
WantedBy=multi-user.target
[root@tomcat ~]# systemctl daemon-reload
// 验证
[root@tomcat ~]# systemctl enable tomcat.service --now
Created symlink /etc/systemd/system/multi-user.target.wants/tomcat.service → /usr/lib/systemd/system/tomcat.service.
[root@tomcat ~]# ss -ntulp |grep java
tcp LISTEN 0 1 [::ffff:127.0.0.1]:8005 *:* users:(("java",pid=1734,fd=77))
tcp LISTEN 0 100 *:8009 *:* users:(("java",pid=1734,fd=53))
tcp LISTEN 0 100 *:8080 *:* users:(("java",pid=1734,fd=48))
注意:使用systemctl启动的不能使用路径关闭,使用路径启动的不能使用systemctl关闭
管理端网页
// 设置管理端网页。但生产环境中为安全一般禁用
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
浏览器访问 http://192.168.88.88:8080
可以通过Server Status、Manager App、Host Manager管理Tomcat
Tomcat 8.5以后,默认只能本地环回地址访问127.0.0.1
// 开启功能并设置用户密码
[root@tomcat tomcat]# vim conf/tomcat-users.xml // 管理网页配置文件
39 <role rolename="manager-gui"/> # 开启功能
40 <role rolename="admin-gui"/> # 开启功能
41 <user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/> # 设置用户密码
42 </tomcat-users>
[root@tomcat tomcat]# bin/shutdown.sh
[root@tomcat tomcat]# bin/startup.sh
// Tomcat 8.5默认限制管理端的访问IP
[root@tomcat tomcat]# curl -u tomcat:tomcat http://127.0.0.1:8080/manager/status
[root@tomcat tomcat]# curl -u tomcat:tomcat http://127.0.0.1:8080/host-manager/html
[root@tomcat tomcat]# curl -u tomcat:tomcat http://127.0.0.1:8080/host-manager/html // 403
// 修改访问IP的限制,不是Tomcat的配置文件所以修改后不用重启服务
[root@tomcat tomcat]# find . -name context.xml
./conf/context.xml
./webapps/host-manager/META-INF/context.xml
./webapps/manager/META-INF/context.xml
[root@tomcat tomcat]# less webapps/host-manager/META-INF/context.xml
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> # 8.5版本之后限制只能通过127.0.0.1访问
[root@tomcat tomcat]# less webapps/manager/META-INF/context.xml
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> # 8.5版本之后限制只能通过127.0.0.1访问
[root@tomcat tomcat]# sed -i 's,"127,"\\d,' webapps/host-manager/META-INF/context.xml webapps/manager/META-INF/context.xml
[root@tomcat tomcat]# curl -u tomcat:tomcat http://127.0.0.1:8080/host-manager/html // 正常
// 生产环境中禁用管理端网页
[root@tomcat tomcat]# vim conf/server.xml // 注释或删除conf/tomcat-users.xml相关的配置
<!--
<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>
-->
[root@tomcat tomcat]# bin/shutdown.sh
[root@tomcat tomcat]# bin/startup.sh
[root@tomcat tomcat]# curl -u tomcat:tomcat http://127.0.0.1:8080/host-manager/html // 404
主配置文件server.xml
[root@tomcat tomcat]# cat conf/server.xml # 去除注释之后的有效内容
<?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>
<!-- <GlobalNamingResources>是tomcat-users.xml的相关配置,生产环境中注释或删除-->
<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="localhost">
<!-- defaultHost指定默认的虚拟主机 -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- <Host>虚拟主机,name域名,appBase网页目录,unpackWARs是否自动解WAR包,autoDeploy是否自动部署(加载到jvm中)-->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<!-- directory访问日志目录,prefix访问日志前缀,suffix访问日志后缀,pattern访问日志内容格式
这里"是双引号,已经使用了一层双引号
如果增加内容并想使用双引号:pattern="%h %l %u %t "%r" %s %b "%{User-Agent}i"" -->
</Host>
</Engine>
</Service>
</Server>
8005是shutdown端口:`telnet 127.0.0.1 8005`后输入SHUTDOWN
8080是HTTP端口
8443是HTTPS端口,默认关闭
8009是AJP端口,用于tomcat和apache的交互,8.5版本后默认关闭
Tomcat处理用户请求的流程:
用户发出请求——》请求达到Tomcat——》<Connector>处理http请求——》交给<Engine>的虚拟主机<Host>处理——》处理完成返回给用户
日志格式
访问日志内容的格式 | Tomcat | Nginx |
---|---|---|
客户端IP地址 | %h | $remote_addr |
用户名 | %l | |
用户名 | %u | |
日期和时间 | %t | $local_time |
用户请求的起始行(请求方法和URI) | %r | $request |
状态码 | %s | $status |
服务端响应的大小 | %b | $body_bytes_sent |
用户从哪里跳转来的 | %{Referer}i | $http_referer |
用户的客户端 | %{User-Agent}i | $http_user_agent |
用户的真实IP地址 | %{X-Forwarded-For}i | $http_x_forwarded_for |
虚拟主机<Host>
<Engine>
中的<Host>
中的参数:name
:虚拟主机名称,也是域名。appBase
:网页目录。unpackWARs
:是否自动解WAR包,默认为true。autoDeploy
:是否自动部署,默认为true。
- 注意:如果配置了[降权启动](# 降权启动(监牢模式)),那么要修改数据的所有者所属组。
// 虚拟主机<Host>
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
// 配置
[root@tomcat tomcat]# cp server.xml{.default,}
[root@tomcat tomcat]# vim conf/server.xml // 在<Engine>添加两个虚拟主机
<Engine name="Catalina" defaultHost="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>
<Host name="www.a.com" appBase="test_a"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="test_logs"
prefix="test_access_log" suffix=".log"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
<Host name="www.b.com" appBase="test_b"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="test_logs"
prefix="test_access_log" suffix=".log"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
[root@tomcat tomcat]# bin/shutdown.sh > /dev/null
[root@tomcat tomcat]# bin/startup.sh > /dev/null
// 创建index.jsp。注意:如果部署了降权启动(假设运行用户为tomcat),那么要修改数据的所有者所属组
[root@tomcat tomcat]# mkdir -p test_{a,b}/ROOT
[root@tomcat tomcat]# mkdir test_logs
[root@tomcat tomcat]# echo 'test_a~~' > test_a/ROOT/index.jsp
[root@tomcat tomcat]# echo 'test_b~~' > test_b/ROOT/index.jsp
注意:如果部署了降权启动(假设运行用户为tomcat),那么要修改数据的所有者所属组
// 验证
客户端(192.168.88.5):
[root@client ~]# echo '192.168.88.88 www.a.com www.b.com' >> /etc/hosts
[root@client ~]# curl www.a.com:8080
test_a~~
[root@client ~]# curl www.b.com:8080
test_b~~
[root@tomcat tomcat]# cat test_logs/test_access_log.2023-10-25.log
192.168.88.5 - - [25/Oct/2023:18:58:41 +0800] "GET / HTTP/1.1" 200 9
192.168.88.5 - - [25/Oct/2023:18:58:45 +0800] "GET / HTTP/1.1" 200 9
访问路径<Context>
- 注意:如果配置了[降权启动](# 降权启动(监牢模式)),那么要修改数据的所有者所属组。
<Engine>
中的<Host>
中的属性 appBase 的使用:- 不定义appBase,指向
Tomcat/webapps
appBase=""
,指向Tomcat/
appBase="相对路径"
,指向Tomcat/相对路径
appBase="绝对路径"
,指向绝对路径/
- 不定义appBase,指向
<Host> 属性appBase不使用 <Context> | IP地址:8080 | IP地址:8080/test/,这里test是目录 | IP地址:8080/test,这里test是文件 |
---|---|---|---|
不定义,为webapps | webapps/ROOT/index.jsp | webapps/test/index.jsp | webapps/ROOT/test |
空 "" | ROOT/index.jsp | test/index.jsp | ROOT/test |
相对路径 "app" | app/ROOT/index.jsp | app/test/index.jsp | app/ROOT/test |
绝对路径 "/app" | /app/ROOT/index.jsp | /app/test/index.jsp | /app/ROOT/test |
<Engine>
中的<Host>
中的<Context path="/字符串A" docBase="字符串B" />
:- 属性 path(精确匹配,支持
*
和?
)的使用:只有当客户端访问的URI匹配path时,<Context>
才配置生效- path的值可以为空
""
,也可以为"/路径"
(必须以/
开头且不能以/
结尾)
- 当path为空""
时,可以不定义docBase。此时,只有匹配不含目录的URI,<Context>
才配置生效- 当path不为空
"/路径"
时,就必须定义docBase。此时,只有URI匹配"/路径"时,<Context>
才配置生效
- 当path不为空
- 定义了appBase,就必须定义path。即,在
<Context>
中,属性path是必须的。
- path的值可以为空
- 属性 path(精确匹配,支持
path=""
时,只匹配不含目录的URI。
<Context> 属性path(假设appBase=“app”) | <Context> 属性docBase | IP地址:8080 | IP地址:8080/test, 这里test是文件 | IP地址:8080/test/, 这里test是目录 | IP地址:8080/test/abc, path=""不匹配包含目录的URI |
---|---|---|---|---|---|
空 "" | 不定义,为ROOT | app/ROOT/index.jsp | app/ROOT/test | app/test/index.jsp | app/test/abc |
空 "" | 空 "" | app/index.jsp | app/test | app/test/index.jsp | app/test/abc |
空 "" | 相对路径 "doc" | app/doc/index.jsp | app/doc/test | app/test/index.jsp | app/test/abc |
空 "" | 绝对路径 "/doc" | /doc/index.jsp | /doc/test | app/test/index.jsp | app/test/abc |
path="/字符串A"
时,字符串匹配URI。
<Context> 属性path(假设appBase=“app”) | <Context> 属性docBase | IP地址:8080/pa/ | IP地址:8080/pa/test/, 这里test是目录 | IP地址:8080/pa/test, 这里test是文件 |
---|---|---|---|---|
绝对路径 "/pa" | 空 "" | app/index.jsp | app/test/index.jsp | app/test |
绝对路径 "/pa" | 相对路径 "doc" | app/doc/index.jsp | app/doc/test/index.jsp | app/doc/test |
绝对路径 "/pa" | 绝对路径 "/doc" | /doc/index.jsp | /doc/test/index.jsp | /doc/test |
// 访问路径
appBase的使用:
- 不定义appBase,指向 Tomcat/webapps
- appBase="",指向 Tomcat/
- appBase="相对路径",指向 Tomcat/相对路径
- appBase="绝对路径",指向 绝对路径
path(精确匹配,支持*和?)的使用:只有当客户端访问的URI匹配path时,<Context>才配置生效
- path的值可以为空"",也可以为"/路径"(必须以`/`开头且不能以`/`结尾)
org.apache.catalina.core.StandardContext.setPath A context path must either be an empty string or start with a '/' and do not end with a '/'.
- 当path为空""时,可以不定义docBase。此时,只有匹配不含目录的URI,<Context> 才生效。
- 当path不为空"/路径"时,就必须定义docBase。此时,只有URI匹配"/路径"时,<Context> 才生效。
- 定义了appBase,就必须定义path。
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
// 配置
[root@tomcat tomcat]# \cp server.xml{.default,}
[root@tomcat tomcat]# vim conf/server.xml // 配置虚拟主机localhost、tapp{,0,1,2}、tpa{,0,1,2}、tth{0,1,2}
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="app" # 192.168.88.88:80 ——》app/ROOT/index.jsp
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>
<!-- appBase-->
<Host name="tapp" > # tapp:8080 ——》webapps/ROOT/index.jsp
</Host>
<Host name="tapp0" appBase=""> # tapp0:8080 ——》ROOT/index.jsp
</Host>
<Host name="tapp1" appBase="app"> # tapp1:8080 ——》app/ROOT/index.jsp
</Host>
<Host name="tapp2" appBase="/app"> # tapp2:8080 ——》/app/ROOT/index.jsp
</Host>
<!-- path="",可以不定义appBase-->
<Host name="tpa" appBase="app"> # tpa:8080 ——》app/ROOT/index.jsp
<Context path="" />
</Host>
<Host name="tpa0" appBase="app"> # tpa0:8080 ——》app/index.jsp
<Context path="" docBase="" />
</Host>
<Host name="tpa1" appBase="app"> # tpa1:8080 ——》app/doc/index.jsp
<Context path="" docBase="doc" />
</Host>
<Host name="tpa2" appBase="app"> # tpa2:8080 ——》/doc/index.jsp
<Context path="" docBase="/doc" />
</Host>
<!-- path="/pa",必须定义appBase-->
<Host name="tth0" appBase="app"> # tth0:8080/pa/ ——》app/index.jsp,tth0:8080/pa/test/ ——》app/test/index.jsp
<Context path="/pa" docBase="" />
</Host>
<Host name="tth1" appBase="app"> # tth1:8080/pa/ ——》app/doc/index.jsp,tth1:8080/pa/test/ ——》app/doc/test/index.jsp
<Context path="/pa" docBase="doc" />
</Host>
<Host name="tth2" appBase="app"> # tth2:8080/pa/ ——》/doc/index.jsp,tth2:8080/pa/test/ ——》/doc/test/index.jsp
<Context path="/pa" docBase="/doc" />
</Host>
</Engine>
[root@tomcat tomcat]# systemc restart tomcat.service
// 创建index.jsp。注意:如果部署了降权启动(假设运行用户为tomcat),那么要修改数据的所有者所属组
[root@tomcat tomcat]# mkdir -p ROOT
[root@tomcat tomcat]# mkdir -p app/{ROOT,doc}
[root@tomcat tomcat]# mkdir -p /app/ROOT
[root@tomcat tomcat]# mkdir -p /doc/ROOT
[root@tomcat tomcat]# echo 'webapps/ROOT/index.jsp' > webapps/ROOT/index.jsp
[root@tomcat tomcat]# echo 'ROOT/index.jsp' > ROOT/index.jsp
[root@tomcat tomcat]# echo 'app/index.jsp' > app/index.jsp
[root@tomcat tomcat]# echo 'app/ROOT/index.jsp' > app/ROOT/index.jsp
[root@tomcat tomcat]# echo 'app/doc/index.jsp' > app/doc/index.jsp
[root@tomcat tomcat]# echo '/app/ROOT/index.jsp' > /app/ROOT/index.jsp
[root@tomcat tomcat]# echo '/doc/index.jsp' > /doc/index.jsp
[root@tomcat tomcat]# echo '/doc/ROOT/index.jsp' > /doc/ROOT/index.jsp
[root@tomcat tomcat]# mkdir app/test app/doc/test /doc/test
[root@tomcat tomcat]# echo 'app/test/index.jsp' > app/test/index.jsp
[root@tomcat tomcat]# echo 'app/doc/test/index.jsp' > app/doc/test/index.jsp
[root@tomcat tomcat]# echo '/doc/test/index.jsp'> /doc/test/index.jsp
客户端:
[root@client ~]# echo '192.168.88.88 tapp tapp0 tapp1 tapp2' >> /etc/hosts
[root@client ~]# echo '192.168.88.88 tpa tpa0 tpa1 tpa2' >> /etc/hosts
[root@client ~]# echo '192.168.88.88 tth0 tth1 tth2' >> /etc/hosts
[root@client ~]# curl 192.168.88.88:8080
app/ROOT/index.jsp
// 1.验证appBase的使用
[root@client ~]# curl tapp:8080
app/ROOT/index.jsp
[root@client ~]# curl tapp0:8080
ROOT/index.jsp
[root@client ~]# curl tapp1:8080
webapps/ROOT/index.jsp
[root@client ~]# curl tapp2:8080
/app/ROOT/index.jsp
// 2.验证path=""
[root@client ~]# curl tpa:8080
app/ROOT/index.jsp
[root@client ~]# curl tpa0:8080
app/index.jsp
[root@client ~]# curl tpa1:8080
app/doc/index.jsp
[root@client ~]# curl tpa2:8080
/doc/index.jsp
// 3.验证path="/pa"
// 3.1.不匹配path则<Context>不生效
[root@client ~]# curl tth0:8080
app/ROOT/index.jsp
[root@client ~]# curl tth1:8080
app/ROOT/index.jsp
[root@client ~]# curl tth2:8080
app/ROOT/index.jsp
// 3.2.匹配path时<Context>才生效。注意:以`/`结尾则默认为目录,返回该目录下的index.jsp。没有以`/`结尾则为文件,返回该文件。
// 3.2.1.以`/`结尾则默认为目录,返回该目录下的index.jsp
[root@client ~]# curl tth0:8080/pa/
app/index.jsp
[root@client ~]# curl tth1:8080/pa/
app/doc/index.jsp
[root@client ~]# curl tth2:8080/pa/
/doc/index.jsp
[root@client ~]# curl tth0:8080/pa/test/
app/test/index.jsp
[root@client ~]# curl tth1:8080/pa/test/
app/doc/test/index.jsp
[root@client ~]# curl tth2:8080/pa/test/
/doc/test/index.jsp
// 3.2.2.没有以`/`结尾则为文件,返回该文件。
[root@client ~]# curl tth0:8080/pa
[root@client ~]# curl tth1:8080/pa
[root@client ~]# curl tth2:8080/pa
[root@client ~]# curl tth0:8080/pa/test
[root@client ~]# curl tth1:8080/pa/test
[root@client ~]# curl tth2:8080/pa/test
SSL
// SSL(https,8443)
keytool -genkeypair -alias 密钥对别名 -keyalg RSA -keystore 密钥库文件
* keytool命令由jdk提供
* 选项-genkeypair是keytool的选项,用于生成密钥对并存储在密钥库文件中
* 选项-alias是-genkeypair的子选项,定义密钥对别名
* 选项-keyalg是-genkeypair的子选项,指定生成密钥对使用的算法
* 选项-keystore是-genkeypair的子选项,指定存储密钥对的密钥库文件
注意:一个密钥库文件中可以有多个密钥对。
在配置Tomcat的SSL时,
必须:关键字keystoreFile指定密钥库文件,关键字keystorePass指定密钥库文件密码
选用:关键字keyAlias指定密钥库文件中的密钥对(省略默认为第一个密钥对),关键字keyPass指定密钥对密码(省略默认同keystorePass)
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
// 创建密钥对
[root@tomcat tomcat]# keytool -genkeypair -alias testkey -keyalg RSA -keystore key
Enter keystore password: // 设置密钥库文件密码
Re-enter new password: // 重复密码
What is your first and last name?
[Unknown]: xingming
What is the name of your organizational unit?
[Unknown]: gongsi
What is the name of your organization?
[Unknown]: bumen
What is the name of your City or Locality?
[Unknown]: shi
What is the name of your State or Province?
[Unknown]: sheng
What is the two-letter country code for this unit?
[Unknown]: zh
Is CN=xingming, OU=gongsi, O=bumen, L=shi, ST=sheng, C=zh correct?
[no]: y
Enter key password for <testkey> // 设置密钥对密码,回车同密钥库文件密码
(RETURN if same as keystore password):
Re-enter new password: // 重复密码
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore key -destkeystore key -deststoretype pkcs12".
// 配置Tomcat
[root@tomcat tomcat]# echo 'webapps/ROOT/index.jsp' > webapps/ROOT/index.jsp
[root@tomcat tomcat]# \cp conf/server.xml.default conf/server.xml
[root@tomcat tomcat]# vim conf/server.xml
85 <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
86 maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
87 clientAuth="false" sslProtocol="TLS"
88 keystoreFile="/usr/local/tomcat/key" // keystoreFile指定密钥库文件位置,相对路径是Tomcat/
89 keystorePass="123456" // keystorePass指定密钥库文件密码
90 keyAlias="testkey" // keyAlias指定密钥库文件中的密钥对,省略默认为第一个密钥对
91 keyPass="123456" /> // keyPass指定密钥对密码,省略默认同密钥库文件密码
[root@tomcat tomcat]# systemctl restart tomcat.service
客户端:
[root@client ~]# curl -k https://192.168.88.88:8443
webapps/ROOT/index.jsp
解析动态网站
// 解析动态网站
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
浏览器访问 https://www.zrlog.com/ 下载war包
[root@tomcat tomcat]# ls ~/zrlog-2.2.1-efbe9f9-release.war
/root/zrlog-2.2.1-efbe9f9-release.war
[root@tomcat tomcat]# cp ~/zrlog-2.2.1-efbe9f9-release.war webapps/zrlog.war
[root@tomcat tomcat]# ss -ntulp | grep 8080
tcp LISTEN 0 100 *:8080 *:* users:(("java",pid=1460,fd=48))
[root@tomcat tomcat]# ls webapps/
docs examples host-manager manager ROOT zrlog zrlog.war
浏览器访问 http://192.168.88.88:8080/zrlog/,跳转 http://192.168.88.88:8080/zrlog/install/ 选择数据库并安装使用。论坛的文章存储在数据库,图片存储在本地网页目录
Tomcat多实例
// 多实例,在一台服务器运行多个tomcat服务,通过端口区分服务
nginx需要安装使用,可以通过导入配置文件来配置管理多个网站。Tomcat直接解压即用,可以在配置文件中配置多个网站,也可以直接通过多实例来配置管理多个网站。
Tomcat中配置多个虚拟主机:共享一个Tomcat进程和线程池,集中管理维护,共同启动停止。
Tomcat多实例:每一个Tomcat实例启动一个独立的进程和线程池,管理和维护相对独立,单独启动、停止。
// 思路
Tom多实例:已经部署好webapps/zrlog
1.共用jdk环境
2.复制多份tomcat并修改每个tomcat的端口(8080,8005,8443):tomcat_8081 tomcat_8082
3.指定不同的代码目录(推荐每个代码放在tomcat 默认的webapps/ROOT)
tomcat
conf/server.xml(8080,8005)
webapps/zrlog
tomcat_8081
conf/server.xml(8081,8006)
webapps/zrlog
tomcat_8082
conf/server.xml(8082,8007)
webapps/zrlog
// 部署
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
[root@tomcat ]# cd /usr/local
[root@tomcat local]# cp -r tomcat/ tomcat_8081
[root@tomcat local]# cp -r tomcat/ tomcat_8082
[root@tomcat local]# sed -i 's#8080#8081#g' tomcat_8081/conf/server.xml
[root@tomcat local]# sed -i 's#8005#8006#g' tomcat_8081/conf/server.xml
[root@tomcat local]# sed -i 's#8080#8082#g' tomcat_8082/conf/server.xml
[root@tomcat local]# sed -i 's#8005#8007#g' tomcat_8082/conf/server.xml
[root@tomcat local]# grep -P 'port="\d+"' tomcat_808*/conf/server.xml
tomcat_8081/conf/server.xml:<Server port="8006" shutdown="SHUTDOWN">
tomcat_8081/conf/server.xml: <Connector port="8081" protocol="HTTP/1.1"
tomcat_8082/conf/server.xml:<Server port="8007" shutdown="SHUTDOWN">
tomcat_8082/conf/server.xml: <Connector port="8082" protocol="HTTP/1.1"
[root@tomcat local]# tomcat/bin/startup.sh
[root@tomcat local]# tomcat_8081/bin/startup.sh
[root@tomcat local]# tomcat_8082/bin/startup.sh
此时,在192.168.88.88:8080/zrlog/、192.168.88.88:8081/zrlog/、192.168.88.88:8082/zrlog/ 上发表文章,文字存储在同一数据库,图片存储在Tomcat本地目录,可以使用挂载存储服务器,实现静态数据的一致。
监控Java应用
// 监控Java应用
// 命令行工具(由openjdk-devel或jdk-devel提供)
jps -lvm
jmap PID :查看jvm信息、内存信息
jstack PID :查看进程信息
jstat -gc PID :查看进程信息
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
[root@tomcat ~]# jps
2018 Bootstrap
2613 Jps
[root@tomcat ~]# jps -l
2625 sun.tools.jps.Jps
2018 org.apache.catalina.startup.Bootstrap
[root@tomcat ~]# jps -lv
2018 org.apache.catalina.startup.Bootstrap -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp
2637 sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el8_5.x86_64 -Xms8m
[root@tomcat ~]# jps -lvm
2018 org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp
2649 sun.tools.jps.Jps -lvm -Dapplication.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el8_5.x86_64 -Xms8m
[root@tomcat ~]# jstat -gc 2018
[root@tomcat ~]# jstack 2018
[root@tomcat ~]# jmap 2018
// 图形工具
jconsole
jvisualvm
CATALINA_OPTS="$CATALINA_OPTS \
-Dcom.sun.management.jmxremote \ # jmxremote 开启功能
-Dcom.sun.management.jmxremote.port=12345 \ # 指定端口号 12345固定端口 +2个随机端口
-Dcom.sun.management.jmxremote.authenticate=false \ # 是否开启认证功能,一般使用内网访问无需认证
-Dcom.sun.management.jmxremote.ssl=false \ # 是否启用ssl,一般使用内网访问无需ssl
-Djava.rmi.server.hostname=内网IP地址" # 监听的IP,选择内网IP,使用内网访问
Tomcat服务器(192.168.88.88,Tomcat目录为/usr/local/tomcat):
[root@tomcat tomcat]# bin/catalina.sh // 注释行后增加六行
CATALINA_OPTS="$CATALINA_OPTS \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=12345 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Djava.rmi.server.hostname=192.168.88.88"
[root@tomcat tomcat]# ss -ntulp | grep 12345
tcp LISTEN 0 50 *:12345 *:* users:(("java",pid=3010,fd=22))
开发在Windows找到jconsole或jvisualvm所在的路径,运行后使用连接192.168.88.88的12345端口
故障案例
开启自启失败或定时任务重启失败
Tomcat没有创建systemd.service,而是通过在/etc/rc.d/rc.local写入手动启动的命令`Tomcat目录/bin/startup.sh`,开机后Tomcat没有运行。
可能原因:如果JDK不是通过YUM安装的,而是手动安装后自定义PATH,则系统开机或定时任务运行时不会加载自定义的PATH路径,无法检测到JDK,因而无法启动Tomcat
解决方法一:YUM安装JDK
解决方法二:将自定义PATH写入脚本(开机运行的/etc/rc.d/rc.local,或定时任务的自定义脚本),让系统识别JDK后再启动Tomcat
服务器运行时占用大量swap,内存占用较少
服务器运行时占用大量swap,占用物理内存较少
原因:代码问题,需要查看并修改程序代码
临时解决:临时增大swap以免服务器宕机,再通过调整内核参数使系统优先使用物理内存
[root@tomcat ~]# echo 'vm.swappiness = 0' >> /etc/sysctl.conf // vm.swappiness是swap亲和性,越大越优先使用swap,越小越优先使用内存
[root@tomcat ~]# sysctl -p
高负载排查
高负载情况的排查 | 命令 |
---|---|
1.整体排查,找到出问题的进程PID | ps aux 或top |
2.查看问题进程下的线程负载情况,找到出问题的线程PID | top -Hp 线程PID |
3.将线程的PID转换为十六进制的NID | `echo ‘obase=16;线程PID’ |
4.查看问题进程的详细信息,过滤具体的线程 | `jstack 进程PID |
5.查看jvm的内存使用情况 | jmap -heap 进程PID |
6.导出jvm的内存使用情况 | jmap -dump:format=b,file=/root/tomcatjvm.bin 进程PID |
7.将导出文件给开发人员 | 开发人员通过Windows的mat软件(Eclipse Memory Analyzer Tool)分析 |
// 高负载排查
// 1.整体排查,找到出问题的进程PID
[root@tomcat ~]# top 或 ps aux // 假设高负载的是Tomcat604
……省略一万字
[root@tomcat ~]# jps // 如果确定是java的进程也可以通过jps查看PID
744 Jps
604 Bootstrap
// 2.查看问题进程下的线程负载情况,找到出问题的线程PID
[root@tomcat ~]# top -Hp 604 // 假设高负载的是线程605
……省略一万字
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
604 root 20 0 2937200 250820 18700 S 0.0 12.7 0:00.00 java
605 root 20 0 2937200 250820 18700 S 80.0 12.7 0:00.55 java
……省略一万字
// 3.线程的PID转换为十六进制的NID
[root@tomcat ~]# yum install -y bc
[root@tomcat ~]# echo 'obase=16;605' | bc // jstack中的线程PID是十六进程的NID
25D
// 4.查看问题进程的详细信息,过滤具体的线程
[root@tomcat ~]# jstack 604
……省略一万字
"main" #1 prio=5 os_prio=0 tid=0x00007f59b404d800 nid=0x25d runnable [0x00007f59bb59f000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:560)
at java.net.ServerSocket.accept(ServerSocket.java:528)
……省略一万字
[root@tomcat ~]# jstack 604| grep -i -A2 25D // 过滤进程信息中的线程信息
"main" #1 prio=5 os_prio=0 tid=0x00007f59b404d800 nid=0x25d runnable [0x00007f59bb59f000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
// 5.查看jvm的内存使用情况
[root@tomcat ~]# jmap -heap 604
Attaching to process ID 604, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.332-b09
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration:
……省略一万字
Heap Usage:
……省略一万字
11209 interned Strings occupying 961752 bytes.
// 6.导出jvm的内存使用情况
[root@tomcat ~]# jmap -dump:format=b,file=/root/tomcatjvm.bin 604
Dumping heap to /root/tomcatjvm.bin ...
Heap dump file created
[root@tomcat ~]# ll /root/tomcatjvm.bin
-rw------- 1 root root 120920576 Oct 26 16:52 /root/tomcatjvm.bin
[root@tomcat ~]# file /root/tomcatjvm.bin
/root/tomcatjvm.bin: Java HPROF dump, created Thu Oct 26 08:52:57 2023
// 7.将导出文件给开发人员,开发人员通过Windows的mat软件(Eclipse Memory Analyzer Tool)分析
Tomcat优化
安全优化
修改shutdown端口
[root@tomcat tomcat]# vim conf/server.xml
<Server port="8005" shutdown="SHUTDOWN"> # 修改前
<Server port="8555" shutdown="dangerous"> # 修改后
修改AJP端口
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> # 修改前 // 8.5起已经被注释
禁用管理端
// 注释或删除server.xml中关于管理端的配置
[root@tomcat tomcat]# vim conf/server.xml
<!--
<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>
-->
[root@tomcat tomcat]# systemctl restart tomcat.service
// 删除管理端的相关文件
[root@tomcat tomcat]# rm -rf webapps/host-manager
[root@tomcat tomcat]# rm -rf webapps/manager
降权启动(监牢模式)
- linux中 1-1024是特权端口,只能root管理与运行,因此如果nginx要使用非root启动则需要修改监听端口。
让普通用户启动和运行服务:
1.root关闭tomcat
2.修改tomcat目录的所有者所属组
3.授权用户使用systemctl重启tomcat
4.修改systemctl的运行用户
5.如果不配置3、4,也可以使用路径启动关闭服务
// 1.root关闭tomcat,根据对应的启动方式关闭
[root@tomcat ~]# systemctl stop tomcat.service
[root@tomcat ~]# /usr/local/tomcat/bin/shutdown.sh
[root@tomcat ~]# ss -ntulp | grep 8080
[root@tomcat ~]# jps
1426 Jps
// 2.修改tomcat目录的所有者所属组
[root@tomcat ~]# useradd tomcat
[root@tomcat ~]# chown -R tomcat:tomcat /usr/local/tomcat/
// 3.授权用户使用systemctl重启tomcat
[root@tomcat ~]# visudo // 末尾增加一行
tomcat ALL=(ALL) NOPASSWD:/bin/systemctl restart tomcat
// 4.修改systemctl的运行用户
[root@tomcat ~]# vim /usr/lib/systemd/system/tomcat.service // 在[Service]内增加一行
[Service]
User=tomcat
[root@tomcat ~]# systemctl daemon-reload
// 验证
[root@tomcat ~]# su - tomcat
[tomcat@tomcat ~]$ sudo systemctl restart tomcat.service
[tomcat@tomcat ~]$ ss -ntulp | grep 8080
tcp LISTEN 0 100 *:8080 *:* users:(("java",pid=1630,fd=52))
[tomcat@tomcat ~]$ jps
1660 Jps
1630 Bootstrap
[tomcat@tomcat ~]$ logout
[root@tomcat ~]# systemctl stop tomcat.service
[root@tomcat ~]# ps aux | grep java // 可以看到tomcat运行tomcat
// 如果不配置3、4,也可以使用路径启动关闭服务
[tomcat@tomcat ~]$ /usr/local/tomcat/bin/startup.sh
[tomcat@tomcat ~]$ ss -ntulp | grep 8080
tcp LISTEN 0 100 *:8080 *:* users:(("java",pid=1761,fd=52))
[tomcat@tomcat ~]$ jps
1761 Bootstrap
1791 Jps
隐藏版本信息
与Nginx不同,Tomcat需要手动修改涉及版本信息的所有网页文件
修改响应头信息
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1" // 修改前
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1" // 修改后
Server="Nginx/1.20.2" // 修改响应头信息
connectionTimeout="20000"
redirectPort="8443" />
[root@tomcat tomcat]# systemctl restart tomcat.service
访问限制
[root@tomcat tomcat]# vim conf/server.xml // path匹配URI
<Context path="/admin/" docBase="/home/work/tomcat"
debug="0" reloadable="false" crossContext="true">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="61.148.18.138,61.135.165.*" deny="*.*.*.*"/>
</Context>
[root@tomcat tomcat]# systemctl restart tomcat.service
性能优化
工作模式(io模型)
模式 | 英文 | 含义 |
---|---|---|
bio | blocking io | tomcat 7及之前,同步,阻塞,一个线程处理一个请求,缺点:并发量高时,线程数较多,浪费资源。 |
nio | new io | tomcat 8及以后,异步,非阻塞,nio1(默认)、 nio2,可以通过少量的线程处理大量的请求 |
apr | Apache Portable Runtime | 应对高并发场景 Tomcat对静态文件的处理性能。Tomcat apr也是在Tomcat上运行高并发应用的首选模式 |
// 查看效果
[root@tomcat tomcat]# vim logs/catalina.out
30-Nov-2021 11:17:14.578 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1" // 修改前
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol" // 修改后。修改工作模式
connectionTimeout="20000"
redirectPort="8443" />
[root@tomcat tomcat]# systemctl restart tomcat.service
禁用DNS逆向解析
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1" // 修改前
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1" // 修改后
enableLookups="false" // 禁用DNS逆向解析
connectionTimeout="20000"
redirectPort="8443" />
[root@tomcat tomcat]# systemctl restart tomcat.service
开启压缩功能gzip
nginx使用gzip on;
tomcat使用compression;
compression="on" # 开启压缩功能静态文本资源 html js css
compressionMinSize="2048" # 压缩文件,最小2048字节要求(未达2048字节不压缩)
compressableMimeType="text/html,text/plain,text/css,application/javascript,application/json,application/x-font-ttf,application/x-font-otf" # 压缩哪些类型的文件 文本类型 js,css,html
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1" // 修改前
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1" // 修改后
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/plain,text/css,application/javascript,application/json,application/x-font-ttf,application/x-font-otf"
connectionTimeout="20000"
redirectPort="8443" />
[root@tomcat tomcat]# systemctl restart tomcat.service
验证:浏览器——》F12——》Network——》Response Headers——》Content-Encoding
线程数量
maxThreads="500" # 最大的线程数量 200-400之间
minSpareThreads="10" # 空闲时候最小的线程数量
acceptCount="500" # 当达到最大线程数量时的队列长度,一般与maxThreads一致。多于maxThreads的在acceptCount排队
acceptorThreadCount="2" # 每个线程接受的连接请求数量,一般与cpu核心总数一致或2倍,默认是1
[root@tomcat tomcat]# vim conf/server.xml
<Connector port="8080" protocol="HTTP/1.1" // 修改前
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1" // 修改后
maxThreads="500"
minSpareThreads="10"
acceptCount="500"
acceptorThreadCount="2"
connectionTimeout="20000"
redirectPort="8443" />
[root@tomcat tomcat]# systemctl restart tomcat.service
jvm内存调整
-Xms # start,初始内存
-Xmx # max,最大内存
官方建议 jvm初始内存大小是物理内存1/64,jvm最大内存大小是物理内存的1/4
实际使用 jvm最大内存大小是初始内存大小的一到两倍,因为有gc垃圾回收,差距太大造成浪费
[root@tomcat tomcat]# vim bin/catalina.sh // 增加一行
JAVA_OPTS='-Xms1024m -Xmx1024m -Xloggc:/var/log/tomcat_gc.log'