背景
Tomcat是一个非常重要的Web Server,已经存在多年。尤其是最近几年,因为Spring MVC或是Spring Boot的盛行,Tomcat的地位越发重要,地位明显升级。
我相信很多人一般只是停留在使用的基础上,但是想利用Tomcat实现一些复杂的场景或者高级同功能,我们就需要进一步学习,也需要我们把Tomcat的基础弄清楚。
本文将通过大量代码和实例详细讲解Tomcat的基础知识,以便我们对Tomcat有个一个整体深入的认识。
本文的代码基于Tomcat 9. 代码地址在
Tomcat和Catalina的来历
大家都在使用Tomcat,但是我问Tomcat的来历是什么,为什么要会用Catalina,我认为不是每个人都知道。是的,Tomcat和Catalina是2个非常重要的名词,在Tomcat软件和配置里随处可见,但是为什么作者取这样的名字呢?据说,当时作者Craig写代码时,家里的猫有时候不停的跳来跳去,所以最后有Tomcat的取名,那Catalina呢?作者有一个非常喜欢的岛,叫Catalina Island,据说这个岛尽管作者知道,但是还没去过,不知道现在去过没有。所以就用Catalina取名了。
在这个岛上有个镇,叫Avalon,Tomcat曾经取过Avalon的名字,只不过后来放弃了。
Tomcat配置文件
了解Tomcat的结构,最直接的办法是从Tomcat的配置文件看起。下面是Tomcat的配置文件(conf/server.xml),这个是Tomcat源码里的默认配置文件。
为了简单,我删除了一些注释等。
我们可以看到Server、Service、Connector、Engine、Host等。那么它们之间有什么关系呢?
从以上可以看出,Tomcat最顶层的容器叫Server,代表整个服务器,Server中包一个或多个Service,该Service用来代表一个服务。同时,一个Service也包含多个Connector,Connector用来网络连接,例如HTTP,HTTPS或AJP。Service也包含一个或多个Engine等,用来管理和封装Servlet,以及处理具体的请求等。
我们先简要解释一下Tomcat的这个配置文件。
Server
Server的配置文件如下:
port是8005,也就是说端口8005会监听SHUTDOWN的命令,当我们使用tomcat目录下的bin/shutdown.sh去停止Tomcat时,就会往8005端口发送一个SHUTDOWN的命令。当然,如果端口8005关闭了,执行shutdown.sh脚本就会报错,只能kill Tomcat的进程了。
Listener
先看第一个VersionLoggerListener,其实就是打印Tomcat的一些相关信息,例如版本号,OS信息,Java版本信息等。
@Overridepublic void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) { log(); }} private void log() { log.info(sm.getString("versionLoggerListener.serverInfo.server.version", ServerInfo.getServerInfo())); log.info(sm.getString("versionLoggerListener.serverInfo.server.built", ServerInfo.getServerBuilt())); log.info(sm.getString("versionLoggerListener.serverInfo.server.number", ServerInfo.getServerNumber())); log.info(sm.getString("versionLoggerListener.os.name", System.getProperty("os.name"))); log.info(sm.getString("versionLoggerListener.os.version", System.getProperty("os.version"))); log.info(sm.getString("versionLoggerListener.os.arch", System.getProperty("os.arch"))); log.info(sm.getString("versionLoggerListener.java.home", System.getProperty("java.home"))); log.info(sm.getString("versionLoggerListener.vm.version", System.getProperty("java.runtime.version"))); log.info(sm.getString("versionLoggerListener.vm.vendor", System.getProperty("java.vm.vendor"))); log.info(sm.getString("versionLoggerListener.catalina.base", System.getProperty("catalina.base"))); log.info(sm.getString("versionLoggerListener.catalina.home", System.getProperty("catalina.home"))); if (logArgs) { Listargs = ManagementFactory.getRuntimeMXBean().getInputArguments(); for (String arg : args) { log.info(sm.getString("versionLoggerListener.arg", arg)); } } if (logEnv) { SortedMap sortedMap = new TreeMap<>(System.getenv()); for (Map.Entry e : sortedMap.entrySet()) { log.info(sm.getString("versionLoggerListener.env", e.getKey(), e.getValue())); } } if (logProps) { SortedMap sortedMap = new TreeMap<>(); for (Map.Entry
再看看AprLifecycleListener。其实主要是实现在Tomcat的生命周期里,APR的初始化,创建和销毁等。
它们都会实现接口 LifecycleListener。该接口为某些事件定义一个listener,这里的事件,其实就是组件的启动和停止事件。但是组件需要实现Tomcat生命周期的接口。这个在后面讲。
接下来就是GlobalNamingResources,它定义一些全局的JNDI资源,例如数据库连接等,JNDI意思是Java Naming and Directory interface。
然后就是Service,name为Catalina.继续往下看,接下来就是Connector,它主要用来处理网络连接,封装消息包,不同的协议会有不同的处理方式,例如port为8080,协议为HTTP/1.1,会有对应的ProtocolHander来处理,这会在后面解释。可以拥有多个Connector。
下面就是Engine,有点类似虚拟主机的意思,有个defaultHost属性,是指在找不到虚拟主机时用的。Realm主要用来做安全域,后面也会降到。
Valve是Tomcat一个非常重要的概念,和Pipeline配合使用,在这里设置的是一个关于Tomcat log的Valve,主要用于将Tomcat的日志以某种格式打印到往到文件,className是其实现类,prefix是日志文件的前缀,suffix是后缀,2者组合起来就是文件名了。Pattern是日志内容的pattern,可以将request的属性打印到文件里,具体如何打印还得参看Tomcat官方文档。
上面的配置文件只提及到部分组件或标签,后面会介绍更多的配置信息。