Tomcat是一个免费的开放源代码的Web应用服务器,具有处理HTML静态资源页面的功能,同时它还是一个Servlet和JSP的容器。Tomcat于1999年发布,初始版本为3.0,实现了Servlet2.2和JSP1.1的规范。

Tomcat的核心分为3个部分:

  1. Web容器:负责处理静态页面;
  2. JSP容器:把JSP页面翻译为一般的Servlet
  3. Catalina:是一个servlet容器,用于处理servlet

部署服务

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 安装JDK
[root@tomcat]dnf install -y java-17-openjdk java-17-openjdk-devel
[root@tomcat]alternatives --config java
There is 1 program that provides 'java'.

Selection Command
-----------------------------------------------
*+ 1 java-17-openjdk.x86_64 (/usr/lib/jvm/java-17-openjdk-17.0.6.0.9-0.3.ea.el8.x86_64/bin/java)
[root@tomcat]alternatives --config javac

There is 1 program that provides 'javac'.

Selection Command
-----------------------------------------------
*+ 1 java-17-openjdk.x86_64 (/usr/lib/jvm/java-17-openjdk-17.0.6.0.9-0.3.ea.el8.x86_64/bin/javac)

Enter to keep the current selection[+], or type selection number: 1

# 验证JDK
[root@tomcat]java --version
openjdk 17.0.6-ea 2023-01-17 LTS
OpenJDK Runtime Environment (Red_Hat-17.0.6.0.9-0.3.ea.el8) (build 17.0.6-ea+9-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.6.0.9-0.3.ea.el8) (build 17.0.6-ea+9-LTS, mixed mode, sharing)

# 创建java程序并执行
cat > java_test.java << EOF
class java_test {
public static void main(String[] args) {
System.out.println("Hello Java World !");
}
}
EOF
# 执行java程序
[root@tomcat]java java_test.java
Hello Java World !

# 安装Tomcat 10
# 下载
[root@tomcat] curl -O https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.13/bin/apache-tomcat-10.1.13.tar.gz
[root@tomcat] tar zxvf apache-tomcat-10.1.13.tar.gz
# 移动执行文件到目录
[root@tomcat]mv apache-tomcat-10.1.13 /usr/libexec/tomcat
# 新增用户
[root@tomcat]useradd -M -d /usr/libexec/tomcat/ tomcat
# 配置权限
[root@tomcat]chown -R tomcat:tomcat /usr/libexec/tomcat/
# 创建服务配置文件
[root@tomcat]cat > /usr/lib/systemd/system/tomcat.service <<EOF
[Unit]
Description=Apache Tomcat 10
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/libexec/tomcat/bin/startup.sh
ExecStop=/usr/libexec/tomcat/bin/shutdown.sh
RemainAfterExit=yes
User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target
EOF
# 重新加载服务
[root@tomcat]systemctl daemon-reload
[root@tomcat]systemctl enable --now tomcat.service
Created symlink /etc/systemd/system/multi-user.target.wants/tomcat.service →
[root@tomcat]netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 4601/java
tcp6 0 0 :::8080 :::* LISTEN 4601/java
# 配置防火墙
[root@tomcat] firewall-cmd --add-port=8080/tcp --permanent
[root@tomcat] firewall-cmd --reload

index

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
# 测试web-server
[root@tomcat]mkdir /usr/libexec/tomcat/webapps/ROOT/WEB-INF/classes
[root@tomcat]chown -R tomcat:tomcat /usr/libexec/tomcat/
[root@tomcat]cat > /usr/libexec/tomcat/webapps/ROOT/WEB-INF/classes/daytime.java <<EOF
import java.io.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.util.Calendar;

public class daytime extends HttpServlet {
public void doGet(HttpServletRequest request
,HttpServletResponse response)

throws IOException, ServletException{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Calendar cal = Calendar.getInstance();
out.println("<html>\n<head>\n<title>DayTime</title>\n</head>\n<body>");
out.println("<div style=\"font-size: 40px; text-align: center; font-weight: bold\">");
out.println(cal.get(Calendar.YEAR) + "/" + (cal.get(Calendar.MONTH) + 1) + "/" +
cal.get(Calendar.DATE) + " " + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE));
out.println("</div>\n</body>\n</html>");
}
}
EOF
# 运行java程序
[root@tomcat]javac -classpath /usr/libexec/tomcat/lib/servlet-api.jar /usr/libexec/tomcat/webapps/ROOT/WEB-INF/classes/daytime.java
[root@tomcat]cat > /usr/libexec/tomcat/webapps/ROOT/WEB-INF/web.xml <<EOF
<servlet>
<servlet-name>daytime</servlet-name>
<servlet-class>daytime</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>daytime</servlet-name>
<url-pattern>/daytime</url-pattern>
</servlet-mapping>
EOF
# 网页验证
[root@tomcat]lynx http://127.0.0.1:8080/daytime
2022/12/23 13:23

配置管理

目录结构

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
[root@tomcat]tree -d apache-tomcat-10.1.13
apache-tomcat-10.1.13
├── bin # 服务启动、停止等相关程序和文件
├── conf # 配置文件
├── lib # 库目录
├── logs # 日志目录
├── temp # 临时文件目录
├── webapps # 应用程序部署目录
│   ├── docs
│   │   ├── annotationapi
│   │   ├── api
│   │   ├── appdev
│   │   │   └── sample
│   │   │   ├── docs
│   │   │   ├── src
│   │   │   │   └── mypackage
│   │   │   └── web
│   │   │   ├── images
│   │   │   └── WEB-INF
│   │   ├── architecture
│   │   │   ├── requestProcess
│   │   │   └── startup
│   │   ├── config
│   │   ├── elapi
│   │   ├── images
│   │   │   └── fonts
│   │   ├── jaspicapi
│   │   ├── jspapi
│   │   ├── META-INF
│   │   ├── servletapi
│   │   ├── tribes
│   │   ├── WEB-INF
│   │   │   └── jsp
│   │   └── websocketapi
│   ├── examples
│   │   ├── jsp
│   │   │   ├── async
│   │   │   ├── cal
│   │   │   ├── checkbox
│   │   │   ├── colors
│   │   │   ├── dates
│   │   │   ├── error
│   │   │   ├── forward
│   │   │   ├── images
│   │   │   ├── include
│   │   │   ├── jsp2
│   │   │   │   ├── el
│   │   │   │   ├── jspattribute
│   │   │   │   ├── jspx
│   │   │   │   ├── misc
│   │   │   │   ├── simpletag
│   │   │   │   └── tagfiles
│   │   │   ├── jsptoserv
│   │   │   ├── num
│   │   │   ├── security
│   │   │   │   └── protected
│   │   │   ├── sessions
│   │   │   ├── simpletag
│   │   │   ├── snp
│   │   │   ├── tagplugin
│   │   │   └── xml
│   │   ├── META-INF
│   │   ├── servlets
│   │   │   ├── images
│   │   │   └── nonblocking
│   │   ├── WEB-INF
│   │   │   ├── classes
│   │   │   │   ├── async
│   │   │   │   ├── cal
│   │   │   │   ├── checkbox
│   │   │   │   ├── colors
│   │   │   │   ├── compressionFilters
│   │   │   │   ├── dates
│   │   │   │   ├── error
│   │   │   │   ├── examples
│   │   │   │   ├── filters
│   │   │   │   ├── http2
│   │   │   │   ├── jsp2
│   │   │   │   │   └── examples
│   │   │   │   │   ├── el
│   │   │   │   │   └── simpletag
│   │   │   │   ├── listeners
│   │   │   │   ├── nonblocking
│   │   │   │   ├── num
│   │   │   │   ├── sessions
│   │   │   │   ├── trailers
│   │   │   │   ├── util
│   │   │   │   ├── validators
│   │   │   │   └── websocket
│   │   │   │   ├── chat
│   │   │   │   ├── drawboard
│   │   │   │   │   └── wsmessages
│   │   │   │   ├── echo
│   │   │   │   └── snake
│   │   │   ├── jsp
│   │   │   ├── lib
│   │   │   └── tags
│   │   └── websocket
│   ├── host-manager
│   │   ├── css
│   │   ├── images
│   │   ├── META-INF
│   │   └── WEB-INF
│   │   └── jsp
│   ├── manager
│   │   ├── css
│   │   ├── images
│   │   ├── META-INF
│   │   └── WEB-INF
│   │   └── jsp
│   └── ROOT
│   └── WEB-INF
└── work # jsp编译之后的结构文件,需要提前预热访问,升级应用之后,删除此目录才能更新

配置文件

文件名 说明
server.xml 主配置文件
web.xml 定义和部署webapp
context.xml 定义所有web程序均需加载的Context配置
tomcat-user.xml 用户认证的账号和密码文件
catalina.policy 当使用security选项启动tomcat时,用于为tomcat设置安全策略
catalina.properties tomcat环境变量的配置,用于设定类加器路径,以及JVM调优相关参数
logging.properties tomcat日志相关配置,可以修该日志级别和日志路径

核心组件

名称 说明
Server 服务器,tomcat运行的实例
Service 服务,用来组织Engin和Connector的对应关系,一个service中只有一个engine
Connector 连接器,负责客户端的http、https、ajp等协议连接,一个connector只属于某一个engine
Engine 引擎,用来响应并处理用户的请求
Host 虚拟主机,可实现多虚拟主机
Context 应用的上下文,配置特定url路径映射和目录映射关系
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
[root@tomcat]grep -v '\-\-' ./server.xml 
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true"
maxParameterCount="1000"
>
</Connector>
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443"
maxParameterCount="1000"
/>
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>

处理请求

如果用户访问http://localhost:8080/test/index.jsp

  1. 浏览器请求被发送到服务器端口8080,tomcat监听此端口,通过侦听http/1.1/Connector获取请求
  2. Connector将请求交给所在Server的Engine来处理,并等待engine的响应
  3. engine获得请求,遍历它所有虚拟主机hosts
  4. 匹配到localhosts主机,否则交由defaulthost处理
  5. host匹配到路径请求为/test/index.jsp,匹配它所拥有的Context
  6. 请求匹配到path=/test的contest获得请求index.jsp,在它的mapping table中寻找对应的servlet
  7. context匹配到url 参数为*.jsp的servlet,对应于jspservlet类狗仔httpServletRequest对象和HttpServletResponse对象,作为参数调用jspServlet的doGet或者doPost方法
  8. Context把HttpServletResponse对象返回给hosts
  9. host把HttpServletResponse对象返回给engine
  10. engine把HttpServletResponse对象返回给Connector
  11. Connector把HttpServletResponse对象返回给浏览器端

部署站点

配置文件

Tomcat中默认网站根目录是$CATALINA_BASE/webapps/,其下的ROOT目录就是网站默认根目录,webapps下的每一个目录对应一个web应用,即WebAPP。每一个虚拟主机都可以使用appBase指令配置自己的站点目录,使用appBase目录下的ROOT目录为主站目录。webapps目录可能有子目录,但非必须:

  1. 主页配置:默认按照 index.html index.htm index.jsp 顺序查找
  2. WEB-INF: 当前目录webapp的私有资源路径,通常存粹web.xml和context.xml
  3. META-INF: 类似WEB-INF,私有资源配置信息,浏览器无法访问
  4. classes/: 类文件
  5. lib/: 当前应用依赖的jar包

部署方式

  1. WebApp应用的归档格式
    1. .war: 类zip格式文件,包括一个应用的所有资源,例如jsp、html、配置文件等
    2. .jar: EJB类文件打包压缩类zip格式文件,包括很多class文件
    3. .rar: 资源适配器文件,目前已不常用
    4. .ear: 企业级webapp打包,目前已不常用
  2. 部署方式:
    1. 部署Deploy:将webapp源文件放到目标目录,通过web.xml和context.xml文件配置路径访问应用程序
      1. 自动部署
      2. 手动部署:
        1. 冷部署
        2. 热部署
    2. 反部署undeploy
    3. 启动
    4. 停止

构建WAR包部署

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
52
53
54
55
56
57
58
59
60
61
62
63
64
# 创建目录
[root@tomcat ~]# pwd
/root
[root@tomcat ~]# mkdir app
[root@tomcat ~]# cd app
[root@tomcat app]# touch test.html
[root@tomcat app]# touch test.jsp
[root@tomcat app]# cat test.html
<h1>This is Test HTML. </h1>
[root@tomcat app]# cat test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jsp例子</title>
</head>
<body>
后面的内容是服务器端动态生成字符串,最后拼接在一起
<%
out.println("hello jsp");
%>
<br>
<%=request.getRequestURL()%>
</body>
</html>

# 生成war包
[root@tomcat app]# jar cvf app.war *
added manifest
adding: test.html(in = 29) (out= 28)(deflated 3%)
adding: test.jsp(in = 365) (out= 301)(deflated 17%)
[root@tomcat app]# file app.war
app.war: Java archive data (JAR)

# 部署war包
[root@tomcat app]# chown tomcat:tomcat app.war
[root@tomcat app]# cp -p app.war /usr/libexec/tomcat/webapps/
# 自动解压缩
[root@tomcat app]# ll /usr/libexec/tomcat/webapps/
total 12
drwxr-x--- 3 tomcat tomcat 55 Sep 14 12:49 app
-rw-r--r-- 1 tomcat tomcat 886 Sep 14 12:44 app.war
drwxr-x--- 3 tomcat tomcat 4096 Sep 14 12:01 ROOT
[root@tomcat app]# lynx 127.0.0.1:8080/app/test.jsp
后面的内容是服务器端动态生成字符串,最后拼接在一起 hello jsp
http://127.0.0.1:8080/app/test.jsp
[root@tomcat app]# lynx 127.0.0.1:8080/app/test.html
<This is Test HTML.
# 访问之后自动生成.class和.java文件
[root@tomcat app]# tree /usr/libexec/tomcat/work/Catalina/localhost/app/
/usr/libexec/tomcat/work/Catalina/localhost/app/
└── org
└── apache
└── jsp
├── test_jsp.class
└── test_jsp.java

3 directories, 2 files
# 自动卸载
[root@tomcat app]# rm /usr/libexec/tomcat/webapps/app.war
rm: remove regular file '/usr/libexec/tomcat/webapps/app.war'? y
# 之后webapps和work目录下对应app目录自动删除。

管理页面

Tomcat自带管理app,但默认是不许访问,需要配置开启

1
2
3
4
5
6
7
8
9
10
11
# 修改conf/tomcat-user.xml文件
[root@tomcat ~]# vim /usr/libexec/tomcat/conf/tomcat-user.xml
# 在<tomcat-user>之后添加
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="admin" password="123456" roles="manager-gui,admin-gui"/>
[root@tomcat ~]# vim /usr/libexec/tomcat/webapps/manager/META-INF/context.xml
# 允许localhost之外的地址访问
allow="127.0.0.1|192.168.10.* />
# 重启服务
[root@tomcat ~]# systemctl restart tomcat
后台管理页面

manager

服务状态页面

manager

其他配置

多主机设置

配置说明

  1. name必须是主机名,用主机名来匹配
  2. appBase当前主机的站点根目录
  3. unpackWARs 是否自动解压war格式
  4. autoDeploy 热部署,自动加载并运行应用

配置过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 添加多站点配置文件
[root@tomcat ~]# vim /usr/libexec/tomcat/conf/server.xml
# 在localhost之后添加
<Host name="1.sujx.net" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="sujx.net_1_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
<Host name="2.sujx.net" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="sujx.net_2_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
# 创建目录
[root@tomcat ~]# mkdir -p /data/web{1,2}/ROOT/
# 将前述test.jsp文件复制为index.jsp
[root@tomcat ~]# cp index.jsp /data/web{1,2}/ROOT/

8005端口配置

8005端口是tomcat的管理端口,默认监听在127.0.0.1上,无需验证即可发送SHUTDOWN整个字符串来关闭tomcat。

1
2
[root@tomcat ~]# vim /usr/libexec/tomcat/conf/server.xml 
<Server port="8005" shutdown="SHUTDOWN">

该配置项无法注释,否则无法启动tomcat。解决措施有:

  1. 修改SHUTDOWN字符串为随机字符;
  2. 修改端口号为0,会使用随机端口;
  3. 修改端口号为-1,将关闭该功能。

修改服务器版本信息

默认不显示tomcat的HTTP的Server头信息,可以修改为其他值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@tomcat ~]# vim /usr/libexec/tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
server="sujx home lab" # 新增配置项
/>
[root@tomcat ~]# http 127.0.0.1:8080
HTTP/1.1 200
Connection: keep-alive
Content-Type: text/html;charset=UTF-8
Date: Thu, 14 Sep 2023 07:53:47 GMT
Keep-Alive: timeout=20
Server: sujx home lab
Transfer-Encoding: chunked

修改访问端口为80

Tomcat用户运行时不能直接使用1024以下端口,需要修改tomcat的运行身份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 将8080修改为80端口
[root@tomcat ~]# vim /usr/libexec/tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>
# 修改程序运行用户
[root@tomcat ~]# vim /lib/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat 10
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/libexec/tomcat/bin/startup.sh
ExecStop=/usr/libexec/tomcat/bin/shutdown.sh
RemainAfterExit=yes
# User=tomcat
# Group=tomcat

[Install]
WantedBy=multi-user.target