本系列文章是基于tomcat6.0的源码。
首先分析一下tomcat的启动脚本,windows下的bat,
if "%OS%" == "Windows_NT" setlocal rem --------------------------------------------------------------------------- rem Start script for the CATALINA Server rem --------------------------------------------------------------------------- rem Guess CATALINA_HOME if not defined set "CURRENT_DIR=%cd%" if not "%CATALINA_HOME%" == "" goto gotHome set "CATALINA_HOME=%CURRENT_DIR%" if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome cd .. set "CATALINA_HOME=%cd%" cd "%CURRENT_DIR%" :gotHome if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome echo The CATALINA_HOME environment variable is not defined correctly echo This environment variable is needed to run this program goto end :okHome set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat" rem Check that target executable exists if exist "%EXECUTABLE%" goto okExec echo Cannot find "%EXECUTABLE%" echo This file is needed to run this program goto end :okExec rem Get remaining unshifted command line arguments and save them in the set CMD_LINE_ARGS= :setArgs if ""%1""=="""" goto doneSetArgs set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1 shift goto setArgs :doneSetArgs call "%EXECUTABLE%" start %CMD_LINE_ARGS% :end
startup.bat脚本,主要完成了环境变量的检查,该脚本很简单,主要的工作全部委托给了catalina.bat,这个脚本是启动tomcat的主脚本,实际上catalina.bat也是检查各种环境变量的配置,并提供几种操作,比如启动,停止debug模式启动等。鉴于篇幅不一一讲解了,我们只关注一下tomcat启动的main函数在哪?bootstrap.jar就是这个jar包的main函数。org.apache.catalina.startup这个路径下的Bootstrap类。不过在分析该类时,我们先要熟悉下tomcat的结构是怎样的?
我们讲Tomcat是一个Servlet容器,通过servlet规范对外提供服务,因此Tomcat实质就是一个提供服务的容器,也就是说Tomcat是一个Server,可以把整个Tomcat抽象成一个Server。继续当我们访问某个服务的时候,通常通过浏览器输入一个类似下面的地址http://192.168.8.221:8080/explorer/loginInit.do其中指定了ip地址和端口还有详细的路径,要能对外提供服务必须指定一个自己的端口与外部通讯,然后TCP通过监听该端口,获取指定的消息并解析返回结果,整个过程可惜大概分为两个过程,监听并获取消息,处理消息,因此可以把这两部分分开来看,形成两个不同的组件,监听并获取消息的为Connector,处理消息的为Continer模块。我们知道Tomcat是一个Servlet容器,Servlet规范中曾提过应用上下文的概念,其实可以理解为我们的webapp,因此借鉴这个概念抽象出来Context概念,也是tomcat中的一个组件,为消息处理模块。但是当请求过来的时候我们怎么区分是哪个虚拟主机,来让Container来区分当然可以,不同的虚拟主机都由它来处理,但是将这个虚拟主机抽象出来似乎是个好的思路,tomcat中将该部分抽象为Host,虚拟主机可以简单的理解为域名,假如我们希望将多个域名映射到一个connector的时候,也即一个Connector组件负责和多个虚拟主机对接,也是非常不方便的,再或者我们希望针对这几个Host做一些统一的处理,比如拦截消息,非常不方便,因此tomcat抽象出来一个Engine的概念,由Engine充当Host的父组件,充当一个门面。讲了这么多Tomcat大概的体系结构图就出来了,借用网上别人的一张图:
上面图中的组件要比我们讲的详细的多,但主要的组件就那几个,Server是最顶层的抽象,一个Server下可以有多个Service,一个Service由一个Container和多个Connector组成,Container中有四个子概念,分别为Engine,Host,Context其中还有一个Wrapper图中没有画出来,Wrapper针对的是Servlet的组件。
Tomcat中有那么多的组件,就像一辆汽车,总得有人把他们组装起来才能工作吧,才能称之为Tomcat吧,没错,是需要组装起来,就像有汽车图纸一样,Tomcat中也有这样一张纸,就是server.xml:
<?xml version='1.0' encoding='utf-8'?> <!-- Note: A "Server" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/server.html 注意这里,Server不是一个Container不能在它下面定义Valves(这个东西后面会提到) --> <!-- 顶层容器,配置好监听停止命令的端口--> <Server port="8005" shutdown="SHUTDOWN"> 配置几个必须的监听器,也可以自己实现,最熟悉的是JasperListener,将我们的jsp翻译成servlet来执行 <!--APR library loader. Documentation at /docs/apr.html --> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> <Listener className="org.apache.catalina.core.JasperListener" /> <!-- Prevent memory leaks due to use of particular java/javax APIs--> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html --> <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html --> <!--JNDI使用--> <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> <!-- A "Service" is a collection of one or more "Connectors" that share a single "Container" Note: A "Service" is not itself a "Container", so you may not define subcomponents such as "Valves" at this level. Documentation at /docs/config/service.html 同样Service也不是Container,不能定义子组件Valves --> <Service name="Catalina"> <!--The connectors can use a shared executor, you can define one or more named thread pools--> 这里我们可以定义线程池,如果不定义,tomcat会使用自己的线程池来管理线程 <!-- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/> --> <!-- A "Connector" represents an endpoint by which requests are received and responses are returned. Documentation at : Java HTTP Connector: /docs/config/http.html (blocking & non-blocking) Java AJP Connector: /docs/config/ajp.html APR (HTTP/AJP) Connector: /docs/apr.html Define a non-SSL HTTP/1.1 Connector on port 8080 连接器,消息的接受和发送都有它的一个endpoint来完成,常见的是http连接器 其实还有两种ajp,apr --> 这里配置我们熟悉的端口8080,并且是http连接器 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <!-- A "Connector" using the shared thread pool--> <!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> --> <!-- Define a SSL HTTP/1.1 Connector on port 8443 This connector uses the JSSE configuration, when using APR, the connector should be using the OpenSSL style configuration described in the APR documentation --> <!-- <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> --> <!-- Define an AJP 1.3 Connector on port 8009 --> AJP协议,通常的tomcat需要和web服务器比如apache来集成,那么和apache的通讯就使用到了该connector <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <!-- An Engine represents the entry point (within Catalina) that processes every request. The Engine implementation for Tomcat stand alone analyzes the HTTP headers included with the request, and passes them on to the appropriate Host (virtual host). Documentation at /docs/config/engine.html --> <!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> --> 默认的Host是localhost,很熟悉吧http://localost:8080/index.html <Engine name="Catalina" defaultHost="localhost"> <!--For clustering, please take a look at documentation at: /docs/cluster-howto.html (simple how to) /docs/config/cluster.html (reference documentation) --> <!-- 这个配置项是来配置tomcat集群的 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> --> <!-- The request dumper valve dumps useful debugging information about the request and response data received and sent by Tomcat. Documentation at: /docs/config/valve.html --> <!-- Valve非常重要的一个概念,这是基于将Request和Response当成消息流的方式来做的实现, 将请求流在不同的管道pipeline中流通,其中valve是在pipeline中定义的处理逻辑节点,也即当request 到该valve的时候,会执行其中的逻辑,这是很典型的责任链模式,非常常用,我们项目就是基于这个模式做的 扩展,形成了自己的一套框架。 <Valve className="org.apache.catalina.valves.RequestDumperValve"/> --> <!-- This Realm uses the UserDatabase configured in the global JNDI resources under the key "UserDatabase". Any edits that are performed against this UserDatabase are immediately available for use by the Realm. --> 类似unix中的group,起到一个安全的作用,就是限制权限。 <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> <!-- Define the default virtual host Note: XML Schema validation will not work with Xerces 2.2. --> 虚拟主机,定义app的基础目录,是否解压等信息 <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- 单点登录的valve <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/> --> </Host> </Engine> </Service> </Server>
这个就是图纸,定义了层级关系,初始化的动作由tomcat应用digester完成,简单提下digester是一个解析xml的工具也是apache的,通过定义个xml并且指定规则,来解析相应的xml,有很多优秀的解析xml工具,比如xtream使用起来非常简便。
上面就是tomcat的大体概况,详细的东西还得深入到源码去探究,怎样去下载编译tomcat源码,这里就不介绍了,网上百度一下有很多,善于借鉴别人的经验为己所用,也是一种能力。