【Tomcat】【一】Tomcat结构以及启动过程
1 前言
这节我们来学习下 Tomcat,是一种 Servlet 容器,这节我们主要来看下 Tomcat 的整体结构以及启动过程,本文的整体思路以及内容大部分都是来自《看透 Spring MVC》,大家可以看看。
2 顶层结构
我们先来看下整体的一个结构图:
- Server:Tomcat 中最顶层的容器叫 Server,代表整个服务器,Server 中包含至少一个 Service,用于具体提供服务。
- Service::Service 主要包含两部分:Connector和 Container。
- Connector:用于处理连接相关的事情,并提供 Socket 与 request、response 的转换。
- Container:用于封装和管理 Servlet,以及具体处理request请求。
- 一个Tomcat 中只有一个 Server;一个Server 可以包含多个Service;一个Service 只有一个Container,但可以有多个Connectors(因为一个服务可以有多个连接,如同时提供 http和 https 连接,也可以提供相同协议不同端口的连接);
Tomcat 里的 Server 由 org.apache.catalinastartup.Catalina 来管理,Catalina 是整个 Tomcat的管理类,它里面的三个方法 lad、start、stop 分别用来管理整个服务器的生命周期,load方法用于根据conf/serverxml文件创建Server 并调用Server 的init方法进行初始化,start方法用于启动服务器,stop 方法用于停止服务器,start 和 stop 方法在内部分别调用了 Server 的start和stop方法,load方法内部调用了 Server 的init方法,这三个方法都会按容器的结构逐层调用相应的方法,比如,Server的start 方法中会调用所有的Service 中的start方法,Service 中的start方法又会调用所有包含的Connectors 和Container 的start 方法,这样整个服务器就启动了,init和stop方法也一样,这就是 Tomcat生命周期的管理方式,更加具体的内容见7.2节。Catalina还有个方法也很重要,那就是 await方法,Catalina 中的await 方法直接调用了Server的await 方法,这个方法的作用是进入一个循环,让主线程不会很出。
不过Tomcat的人口main方法并不在Catalina类里,而是在orgapache.catalina.startupBootstrap中。Bootstrap 的作用类似一个CatalinaAdaptor,具体处理过程还是使用Catalina来完成的,这么做的好处是可以把启动的入口和具体的管理类分开,从而可以很方便地创建出多种启动方式,每种启动方式只需要写一个相应的CatalinaAdaptor 就可以了。
3 Bootstrap的启动过程
Bootstrap是 Tomcat的人口,正常情况下启动Tmcat 就是调用的Bootstrap的main方法其代码如下:
可以看到这里的 main 非常简单,只有两部分内容:首先新建了 Bootstrap,并执行 init方法初始化;然后处理main方法传入的命令,如果args 参数为空,默认执行start。
在init 方法里初始化了 ClassLoader,并用ClassLoader 创建了Catalina实例,然后赋给catalinaDaemon变量,后面对命令的操作都要使用catalinaDaemon来具体执行。
对start命令的处理调用了三个方法:setAwait(true)、load(args)和 start()。这三个方法内部都调用了 Catalina 的相应方法进行具体执行,只不过是用反射来调用的。start 方法(另外两个方法会处理一些参数,调用方法类似)的代码如下:
这里首先判断catalinaDaemon 有没有初始化,如果没有则调用 init 方法对其进行初始化,然后使用 Method 进行反射调用 Catalina 的start 方法。Method 是java.lang.reflect 包里的类代表一个具体的方法,可以使用其中的 invoke 方法来执行所代表的方法,invoke 方法有两个参数,第一参数是Method 方法所在的实体,第二个参数是可变参数用于 Method 方法执行时所需要的参数,所以上面的调用相当于((Catalina)catalinaDaemon).start()。setAwait 和 load也用类似的方法调用了Catalina中的setAwait 和load方法。
4 Catalina的启动过程
从前面的内容可以知道,Catalina 的启动主要是调用 setAwait、load 和start方法来完成的setAwait方法用于设置Server 启动完成后是否进入等待状态的标志,如果为true 则进人,否则不进入;load 方法用于加载配置文件,创建并初始化Server;start 方法用于启动服务器。下面分别来看一下这三个方法
首先来看setAwait 方法,代码如下:
这个方法非常简单,就是设置await 属性的值,await 属性会在 start 方法中的服务器启动完之后使用它来判断是否进入等待状态。
Catalina的load 方法根据 conf/server.xml创建了 Server 对象,并赋值给 server 属性(具体解析操作是通过开源项目 Digester 完成的)然后调用了 server 的init 方法,代码如下:
Catalina的 start 方法主要调用了 server 的start 方法启动服务器,并根据await 属性判断是否让程序进人了等待状态,代码如下:
这里首先判断 Server 是否已经存在了,如果不存在则调用 load 方法来初始化Server,然后调用 Server的 start 方法来启动服务器,最后注册了关闭钩子并根据 await 属性判断是否进人等待状态,之前我们已将这里的 await 属性设置为 true 了,所以需要进入等待状态。进入等待状态会调用await和 stop 两个方法,await 方法直接调用了 Server 的await 方法,Server的await 方法内部会执行一个 while 循环,这样程序就停到了await 方法,当await 方法里的while循环退出时,就会执行 stop 方法,从而关闭服务器。
5 Server的启动过程
Server 接口中提供addService(Service service)、removeService(Service service) 来添加和删除 Service,Server 的init 方法和 start 方法分别循环调用了每个 Service 的init 方法和 start方法来启动所有Service。
Server 的默认实现是 orgapache.catalina.core.StandardServer,StandardServer 继承自 LifecycleMBeanBase,LifecycleMBeanBase 又继承自 LifecycleBase,init 和 stat 方法就定义在了 LifecycleBase中,LifecycleBase 里的init 方法和 start 方法又调用initInteal方法和startInternal方法,这两个方法都是模板方法,由子类具体实现,所以调用 StandardServer 的init和start 方法时会执行StandardServer 月己的initInternal和 startInternal方法,这就是 Tomcat 生命周期的管理方式更详细的过程见7.2节。StandardServer 中的initInternal和 startIntermal方法分别循环调用了每一个service的start和init 方法,代码如下:
除了startInternal和initInternal方法,StandardServer 中还实现了await方法,Catalina 中就是调用它让服务器进入等待状态的,其核心代码如下:
await 方法比较长,为了便于大家理解,这里省略了一些处理异常、关闭 Socket 以及对接收到数据处理的代码。处理的大概逻辑是首先判断端口号 port,然后根据 port 的值分为三种处理方法:
- port为-2,则会直接退出,不进入循环。
- port 为-1,则会进入一个 while ( !stopAwait)的循环,并且在内部没有 break 跳出的语句,stopAwait标志只有调用了 stop 方法才会设置为 true,所以port 为-1时只有在外部调用stop方法才会退出循环。
- port为其他值,则也会进入一个 while (!stopAwait)的循环,不过同时会在 port 所在端口启动一个ServerSocket 来监听关闭命令,如果接收到了则会使用 break 跳出循环。
这里的端口 port和关闭命令 shutdown 是在conf/serverxml文件中配置Server 时设置的默认设置如下:
这时会在8005端口监听”SHUTDOWN”命令,如果接收到了就会关闭 Tomcat。如果不想使用网络命令来关闭服务器可以将端口设置为-1。另外await 方法中从端口接收到数据后还会进行简单处理,如果接收到的数据中有 ASCII码小于32的(ASCII 中32以下的为控制符)则会从小于32 的那个字符截断并丢弃后面的数据,为了便于大家理解这部分代码我们在上面省略掉了。
6 Service的启动过程
Service 的默认实现是orgapache.catalina.core.StandardService,StandardService 也继承自LifecycleMBeanBase 类,所以init 和start 方法最终也会调用nitInternal 和 startInternal方法我们来看一下这两个方法的核心内容(省略了异常处理和日志打印代码)。
可以看到,StandardService中的initInternal和 startInternal方法主要调用 container、executorsmapperListener、connectors 的 init 和 start 方法。这里的 container 和 connectors 前面已经介绍过mapperListener 是 Mapper 的监听器,可以监听 container 容器的变化,executors 是用在 connectors中管理线程的线程池,在 serverx.xml配置文件中有参考用法,不过默认是注释起来的,打开注释就可以看到其使用方法,如下所示:
这样Connector就配置了一个叫 tomcatThreadPool的线程池,最多可以同时启动150个线程,最少要有4个可用线程。
现在整个Tomcat 服务器就启动了,整个启动流程如图所示:
7 小结
书上讲的很透彻,看完这节大概要知道从外到内组成部分:Server->Service->Connector、Container,Server只有一个,一个Server可以有多个Service,一个Service内部又有多个Connector和一个Container,有理解不对的地方欢迎指正哈。