历史

起始于SUN的一个Servlet的参考实现项目Java Web Server,作者是James Duncan Davidson,后将项目贡献给了ASF。和ASF现有的项目合并,并开源成为顶级项目,官网http://tomcat.apache.org

Tomcat仅仅实现了Java EE规范中与Servlet. JSP相关的类库,是JavaEE不完整实现。

著名图书出版商O’Reilly约稿该项目成员,Davidson希望使用一个公猫作为封面,但是公猫已经被另一
本书使用,书出版后封面是一只雪豹。

1999年发布初始版本是 Tomcat3.0,实现了 Servlet2.2和sP1.1规范。

Tomcat4x发布时,内建了 Catalina( Servlet容器)和 asper (SP engine)等

商用的有 BM Web Sphere、 Oracle WebLogic(原属于BEA公司)、 Oracle oc4、 Glassfish、 jBoss等。
开源实现有 Tomcat、 Jetty、 Resin。

安装

略

目录结构

目录 说明
bin 服务启动、停止相关
conf 配置文件
lib 库目录
logs 日志目录
webapps 应用程序,应用部署目录
work jsp编译后的结果文件

配置文件

文件名 说明
server.xml 主配置文件
web.xml 每个webapp只有 “部署”后才能被访问,它的部署方式通常由web.xml进行定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认部署相关的配置
content.xml 每个webapp都可以专用的配置文件,它通常由专用的配置文件context.xml来定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认配置
tomcat-users.xml 用户认证的账号和密码文件
catalina.policy 当使用-security选项启动tomact时,用于为tomcat设置安全策略
catalina.properties Java属性的定义文件,用于设定类加载器路径,以及一些语JVM调优相关参数
logging.properties 日志系统相关的配置。log4j

组件分类

顶级组件

Server,代表整个Tomcat容器

服务类组件

Service,组织Engine和Connector,里面只能包含一个Engine

连接器组件

Connector,有HTTP、HTTPS、AJP协议的连接器

容器类

Engine、Host、Context都是容器类组件,可以嵌入其它组件,内部配置如何运行应用程序。

内嵌类

可以内嵌到其他组件内,valve、logger、realm、loader、manager等。以logger举例,在不同容器组件内定义。

集群类组件

listener、cluster

Tomcat内部组成

image-20200716083629756

名称 说明
Server Tomcat运行的进程实例
Connector 负责客户端的HTTP,HTTPS,AJP等协议的连接。一个Connector只属于一个Engine
Service 用来组织Engine和Connector的关系
Engine 响应并处理用户请求。一个引擎上可以绑定多个Connector
Host 虚拟主机
Context 应用的上下文,配置路径映射path->directory

AJP (Apache Jserv protocol)是一种基于TCP的二进制通讯协议。

核心组件

  • Tomcat启动一个Server进程。可以启动多个Server,但一般只启动一个
  • 创建一个Service提供服务。可以创建多个Service,但一般也只创建一个
    • 每个Service中,是Engine和其连接器Connector的关联配置
  • 可以为这个Server提供多个连接器Connector,这些Connector使用了不同的协议,绑定了不同的端口。其作用就是处理来自客户端的不同的连接请求或响应
  • Service内部还定义了Engine,引擎才是真正的处理请求的入口,其内部定义多个虚拟主机Host
    • Engine对请求头做了分析,将请求发送给相应的虚拟主机
    • 如果没有匹配,数据就发往Engine上的defaultHost缺省虚拟主机
    • Engine上的缺省虚拟主机可以修改
  • Host定义虚拟主机,虚拟主机有name名称,通过名称匹配
  • Context定义应用程序单独的路径映射和配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8085" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20088"
redirectPort="8443” />
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost"appBase="webapps"
unpackWARS="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>

举例:
假设来自客户的请求为: http://localhost:8080/test/index.jsp

  • 浏览器端的请求被发送到服务端端口8080,Tomcat进程监听在此端口上。通过侦听的HTTP/1.1Connector获得此请求。
  • Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的响应
  • Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host
  • Engine匹配到名为localhost的Host。即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机
  • localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
  • Host匹配到路径为/test的Context
  • path=/test的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
  • Context匹配到URLPATTERN为*.jsp的servlet,对应于JspServlet类构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用spServlet的doGet或doPost方法。
  • Context把执行完了之后的HttpServletResponse对象返回给Host
  • Host把HttpServletResponse对象返回给Engine
  • Engine把HttpServletResponse对象返回给Connector
  • Connector把HttpServletResponse对象返回给浏览器端

应用部署

根目录

Tomcat中默认网站根目录是CATALINA_BASE/webapps/
在Tomcat中部署主站应用程序和其他应用程序,和之前WEB服务程序不同。

Nginx

假设在nginx中部署2个网站应用eshop、bbs,假设网站根目录是/var/www/html,那么部署可以是这样的。
eshop解压缩所有文件放到/var/www/html/目录下。
bbs的文件放在/var/www/html/bbs下。

Tomcat

Tomcat中默认网站根目录是CATALINA_BASE/webapps/
在Tomcat的webapps目录中,有个非常特殊的目录ROOT,它就是网站默认根目录。
将eshop解压后的文件放到这个ROOT中。
bbs解压后文件都放在CATALINA_BASE/webapps/bbs目录下。

每一个虚拟主机的目录都可以使用appBase配置自己的站点目录,里面都可以使用ROOT目录作为主站目录。

JSP WebApp目录结构

  • 主页配置:一般指定为index.jsp或index.html
  • WEB-INF/:当前WebApp的私有资源路径,通常存储当前应用使用的web.xml和context.xml配置文件
  • META-INF/:类似于WEB-INF
  • classes/:类文件,当前webapp需要的类
  • lib/:当前应用依赖的jar包

主页实验

默认情况下,/usr/local/tomcat/webapps/ROOT/下添加一个index.html文件,观察访问到了什么?
将/usr/local/tomcat/conf/web.xml中的下面<welcome-file-list>标签内容(默认页),复制到/usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml中,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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_4_0.xsd"
version="4.0"
metadata-complete="true">

<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

配置修改后,观察首页变化

WebApp归档格式

  • war: WebApp打包
  • jar:EJB类打包文件
  • rar:资源适配器类打包文件
  • ear:企业级WebApp打包

传统应用开发测试后,通常打包为war格式,这种文件部署到了Tomcat的webapps下,还可以自动展开。

1
<Host name="localhost" appBase="webapps" unpackMARs="true" autoDeploy="true">

部署Deploy

  • 部署:将webapp的源文件放置到目标目录,通过web.xml和context.xml文件中配置的路径就可以访问该webapp,通过类加载器加载其特有的类和依赖的类到JVM上。
    • 自动部署Auto Deploy: Tomcat发现多了这个应用就把它加载并启动起来
    • 手动部署
      • 冷部署:将webapp放到指定目录,才去启动Tomcat
      • 热部署: Tomcat服务不停止,需要依赖manager、ant脚本、tcd (tomcat client deployer)等工具
  • 反部署undeploy:停止webapp的运行,并从JVM上清除已经加载的类,从Tomcat应用目录中移除部署的文件
  • 启动start:是webapp能够访问
  • 停止stop: webapp不能访问,不能提供服务,但是JVM并不清除它

实验

  1. 添加一个文件,test.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ 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>

先把test.jsp放到ROOT下去,试试看,访问http://YourIP:8080/test.jsp 。
立即可以看到,这是通过路径映射找到相应的test.jsp后,转换成test_jsp.java,在编译成test_jsp.class。/usrlloca/tomcat/work/Catalina/localhost/ROOTlorg/apacheljsp下有转换后的文件。

  1. 添加一个应用
1
2
3
4
5
6
7
8
9
10
11
12
cd 

常见开发项目目录组成
mkdir projects/myapp/{WEB-INF,classes,lib} -pv

常见应用首页,内容就用上面的test.jsp
vim projects/myapp/index.jsp

手动复制项目目录到webapps目录下去
cp -r projects/myapp/ /usr/local/tomcat/webapps/

使用http://YourIP:8080/myapp/访问试试看

配置详解

server.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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" />
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>
1
<Server port="8005" shutdown="SHUTDOWN">

8005是Tomcat的管理端口,默认监听在127.0.0.1上。SHUTDOWN这个字符串接收到后就会关闭此
Server。

1
2
3
4
5
#telnet 127.0.0.1 8005
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SHUTDOWN

这个管理功能建议禁用,改shutdown为一串猜不出的字符串。

1
<Server port="8005" shutdown="44ba3c71d57f494992641b258b965f28">

1
2
3
4
5
6
7
8
9
10
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<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>

用户认证,配置文件是conf/tomcat-users.xml。
打开tomcat-users.xml,我们需要一个角色manager-gui。

1
2
3
4
5
6
7
8
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="kinmfer" password="123123" roles="manager-gui,admin-gui"/>
</tomcat-users>

Tomcat启动加载后,这些内容是常驻内存的。如果配置了新的用户,需要重启Tomcat。

文件路径/usr/local/tomcat/webapps/manager/META-INF/context.xml

1
2
3
4
5
<Context antiResourceLocking="false" privileged="true" >
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
<Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

看正则表达式就知道是本地访问了,由于当前访问地址是192.168.x.x,可以修改正则为

1
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|192.*"

再次测试,成功


1
<Service name="Catalina">

一般情况下,一个Server实例配置一个Service,name属性相当于该Service的ID。


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

连接器配置。

redirectPort,如果访问HTTPS协议,自动转向这个连接器。但大多数时候,Tomcat并不会开启
HTTPS,因为Tomcat往往部署在内部,HTTPS性能较差


1
<Engine name="Catalina" defaultHost="localhost">

引擎配置。

defaultHost指向内部定义某虚拟主机。缺省虚拟主机可以改动,默认localhost.。


1
<Host name="localhost"appBase="webapps" unpackWARS="true"autoDeploy="true">

虚拟主机配置。
name必须是主机名,用主机名来匹配。
appBase,当前主机的网页根目录,是相对于CATALINA_HOME,也可以使用绝对路径
unpackWARs是否自动解压war格式
autoDeploy热部署,自动加载并运行应用

虚拟主机配置实验

尝试再配置一个虚拟主机,并将myapp部署到/data/webapps目录下

1
2
<Host name="node1.kinmfer.com" appBase="/data/webapps/" unpackWARS="true"
autoDeploy="false" />
1
2
3
4
5
6
7
常见虚拟主机根目录
mkdir /data/webapps -pv

cp -r ~/ projects/myapp/ /data/webapps/ROOT

bin/shutdown.sh
bin/startup.sh

刚才在虚拟主机中主机名定义tomcat.kinmfer.com/,所以需要主机在本机手动配置一个域名解析。
如果是windows,修改在C:\Windows\System32\drivers\etc下的hosts文件,需要管理员权限。
使用http://tomcat.kinmfer.com:8080/访问试试看。也可以在tomcat的host-manager中观察。

Context配置

Context作用:

  • 路径映射
  • 应用独立配置,例如单独配置应用日志、单独配置应用访问控制
1
<Context path="/test" docBase="/data/test" reloadable="false"/>

path指的是访问的路径
docBase,可以是绝对路径,也可以是相对路径(相对于Host的appBase)
reloadable,true表示如果WEB-NF/classes或META-INF/lib目录下.class文件有改动,就会将WEB应用重新加载,性能消耗很大。生成环境中,会使用false来禁用。

将~/projects/myapp/下面的项目文件复制到/data/下

1
2
3
cp -r ~/projects/myapp /data/myappv1
cd /data
ln -sv myappv1 test

可以修改一下index.jsp方便区别

Tomcat的配置文件server.xml中修改如下

1
2
3
4
<Host name="tomcat.kinmfer.com"  appBase="/data/webapps"
unpackWARs="true" autoDeploy="true">
<Context path="/test" docBase="/data/test" reloadable="false"/>
</Host>

使用http://tomcat.kinmfer.com:8080/test/

**注意:**这里特别使用了软链接,原因就是以后版本升级,需要将软链接指向myappv2,重启Tomcat。
如果新版上线后,出现问题,重新修改软链接到上一个版本的目录,并重启,就可以实现回滚。