您的位置:首页 > 运维架构 > Tomcat

Tomcat结构分析

2016-04-12 16:52 531 查看
来自:http://panlianghui-126-com.iteye.com/blog/810056

Tomcat是一个基于组件的服务器,它的构成组件都是可配置的,其中最外层的组件是Catalina Servlet容器,其它的组件按照一定的格式要求配置在这个容器中

Tomcat各组件是在<Tomcat_HOME>\conf\server.xml文件中配置,其配置文件内容如下

Java代码


<Server port="8005" shutdown="SHUTDOWN">

<Service name="Catalina">

<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<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
-->
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" 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 -->
<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">
-->
<Engine defaultHost="localhost" name="Catalina">

<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<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 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. -->
<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.
-->
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">

<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<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"/>
-->

<Context docBase="TestTomcat" path="/TestTomcat" reloadable="true" source="org.eclipse.jst.j2ee.server:TestTomcat"/><Context docBase="xishuizhipanBlog" path="/xishuizhipanBlog" reloadable="true" source="org.eclipse.jst.jee.server:xishuizhipanBlog"/></Host>
</Engine>
</Service>
</Server>

server.xm文件的基本结构如下:



<Server> 代表了整个Catalina Servlet 容器,它是Tomcat实例的顶层元素。可包含一个或多个<Service>元素

<Service>包含一个<Engine>元素,以及一个或多个<Connector>元素,这些<Connector>元素共享同一个<Engine>元素

<Connector>代表和客户程序实际交互的组件,它负责接收客户请求,以及返回客户响应结果。

<Engine>每个<Service>元素只能包含一个<Engine>元素. <Engine>元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求.

<Host>一个<Engine>元素中可以包含多个<Host>元素.每个<Host>元素定义了一个虚拟主机,它可以包含一个或多个Web应用.

<Contex>每个<Context>元素代表了运行虚拟主机上的但个Web应用.一个<Host>元素中可以包含多个<Context>元素.

组件的关系图如下



下面,介绍Tomcat的基本元素

1.配置Server元素

Server>元素代表了整个Catalina Servler容器,它是Tomcat实例的顶层元素,由org.apache.catalina.Server接口来定义.<Server>元素中可以包含一个或者多个<Service>元素,但<Server>元素不能作为任何其他元素的子元素.

Java代码


<Server port="8005" shutdown="SHUTDOWN">

className :指定实现org.apache.catalina.Server接口的类,默认值为org.apache.catalina.core.StandardServer.

port :指定Tomcat服务器监听shutdown命令的端口.终止Tomcat服务运行时,必须在Tomcat服务器所在的机器上发出Shutdown命令.该属性是必须设定的.

shutdown :指定终止Tomcat服务器运行时,发给Tomcat服务器的shutdown监听端口的字符串.该属性是必须设定的.

2.配置Service元素

<Service>元素由org.apache.catalina.Service接口定义,它把韩一个<Engine>元素,以及一个或多个<Connector>元素,这些<Connector>元素共享一个<Engine>元素.

Java代码


<Service name="Catalina">

<Service>处理所有直接由Tomcat服务器接收的Web客户请求。

className :指定实现org.apache.catalina.Service接口的类,默认值org.apache.catalina.core.StandardService.

name :定义Service的名字.

3.配置Engine元素

<Engine>元素由org.apahe.catalina.Engine接口定义. 每个<Service>元素只能包括一个<Engine>元素. <Engine>元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求.

Java代码


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

className :指定实现org.apache.catalina.Engine接口的类,默认值为org.apache.catalina.core.StandardEngine.

name :定义Engine的名字.

在<Engine>元素中可以包含如下的子元素:

<Logger>

<Realm>

<Valve>

<Host>

4.配置Connector元素

<Connector>元素由org.apache.catalina.Connector接口定义.<Connector>元素代表与客户程序实际交互的组件,它负责接收客户的请求,以及向客户返回响应结果。

Java代码


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

Java代码


<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>

第一个<Connector>元素定义了一个HTTP Connector,它通过8080端口接收HTTP请求;

第二个<Connector>元素定义了一个JK Connector,它通过8009端口接收由其他HTTP服务器(如Apache服务器)转发过来的客户请求.

<Connector>属性含义(共同属性):

--------------------------------------------------------------

className :指定实现org.apache.catalina.Connector 接口的类,默认值为org.apache.catalina.core.StandardConnector.

enableLookups :如果设为true,表示支持域名解析,可以把IP地址解析为主机名.Web应用调用request.getRemostHost方法将返回客户的主机名.该属性默认值为true.

redirectPort :指定转发端口.如果当前端口只支持non-SSL请求,在需要安全通信的场合,将把客户请求转发到基于SSL的redirectPort的端口.

HttpConnector的属性描述如下:

--------------------------------------------------------------

calssName :指定实现org.apache.catalina.Connector接口的类,默认值为org.apache.coyote.tomcat5.CoyoteConnector.

enableLookups :同上.

redirectPort :同上.

prot :设定TCP/IP断口号,默认为8080.

address :如果服务器有两个以上IP地址,该属性可以设定端口监听的IP地址,默认情况下,端口会监听服务器上所有IP地址.

bufferSize :设定由端口创建的输入流的缓存大小,默认值为2048byte.

protocol :设定HTTP协议,默认值为HTTP/1.1.

maxThreads :设定处理客户请求的线程的最大数目,这个值也决定了服务器可以同时响应客户请求的最大数目,默认值为200.

acceptCount :设定在监听端口队列中的最大客户请求数,默认值为10. 如果队列已满,客户请求将被拒绝.

connectionTimeout :定义建立客户连接超时的时间,以毫秒为单位.如果设置为-1,表示不限制建立客户连接的时间.

JK Connector 的属性如下:

--------------------------------------------------------------

className :指定实现org.apache.catalina.Connector接口的类,默认值为org.apache.coyote.tomact5.CoyoteCnnector.

enableLookups :同上.

redirectPort :同上.

port :设定AJP端口号.

protocol :必须设定为AJP/1.3协议.

5.配置<Host>元素

<Host>元素由org.apache.catalina.Host接口定义.一个<Engine>元素可以包含多个<Host>元素.每个<Host>元素定义了一个虚拟主机,它可以包含一个或多个Web应用.

Java代码


<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">

以上代码定义了一个名为localhost的虚拟主机,Web客户访问它的URL为: http://localhost:8080/
className :指定实现org.apache.catalina.Host接口的类,默认值为org.apache.catalina.core.StandardHost.

appBase :指定虚拟主机的目录,可以指定绝对目录,也可以指定相对于<CATALINA_HOME>的相对目录. 如果此项没有设定,默认值为<CATALINA_HOME>/webapps.

unpackWARs :如果此项设为true,表示将把Web应用的WAR文件先展开为开放目录结构后再运行.如果设为false,将直接运行WAR文件.

autoDeploy :如果此项设为true,表示当Tomcat服务器处于运行状态时,能够监测appBase下的文件,如果有新的Web应用加入进来,会自动发布这个Web应用.

alias :指定虚拟主机的别名,可以指定多个别名.

deployOnStartup :如果此项设为true,表示Tomcat服务器启动时会自动发布appBase目录下的所有Web应用,如果Web应用在server.xml中没有相应的<Context>元素,将采用Tomcat默认的Context. deployOnStartup的默认值为true.

name :定义虚拟主机的名字.}

在<Host>元素中可以包含如下的子元素:

<Logger>

<Realm>

<Valve>

<Context>

6.配置Contex元素

<Context>元素由org.apache.catalina.Context接口定义. <Context>元素是使用最频繁的元素. 每个<Context>元素代表了运行在虚拟主机上的单个Web应用. 一个<Host>元素中可以包含多个<Context>元素.

Java代码


<Context docBase="TestTomcat" path="/TestTomcat" reloadable="true" source="org.eclipse.jst.j2ee.server:TestTomcat"/><Context docBase="xishuizhipanBlog" path="/xishuizhipanBlog" reloadable="true" source="org.eclipse.jst.jee.server:xishuizhipanBlog"/></Host>

className :指定实现org.apache.catalina.Context接口的类,默认值为org.apache.catalina.core.StandardContext.

path :指定访问该Web应用的URL入口.

docBase :指定Web应用的文件路径.可以给定绝对路径,也可以给定相对于Host的appBase属性的相对路径. 如果Web应用采用开放目录结构,那就指定Web应用的根目录;如果Web应用是个WAR文件,那就指定WAR文件的路径.

reloadable :如果这个属性设为true,Tomcat服务器在运行状态下会监视在WEB-INF/class和WEB-INF/lib目录下CLASS文件的改动.如果检测到有calss文件被更新,服务器会自动重新加载Web应用.

cookies :指定是否通过Cookie来支持Session,默认为true.

useNaming :指定是否支持JNDI,默认为true.

<----------------------------------------------------------------------------------------------------------------------------->

下面解说下tomcat的启动过程

从开始学习网页编程时,我就一直在疑惑,为什么这个程序不要main函数,直接启动服务器就行了。但是服务器又是怎么启动的?

最近看了一片文档,以及自己查看tomcat源码,才大概了解那么一点。

1.启动tomcat

apache-tomcat-6.0.29\bin\bootstrap.jar

org.apache.catalina.startup.Bootstrap start

Bootstrap是Tomcat的入口。比如启动、关闭、重启都是通过这个类实现对tomcat的控制。

2.ClassLoader的使用

Tomcat对不同的模块可能使用不同的ClassLoader加载。这也就是为什么很多类不在启动的classpath中,却可以被它调用的原因。

下面是Bootstrap初始化ClassLoader的方法:

Java代码


private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}

下图是Tomcat用户手册上看到的。

Bootstrap

|

System

|

Common

/ \

Catalina Shared

(server) / \

Webapp1 Webapp2 ...

Bootstrap是JVM提供的

System是在classpath中提供的

Common包含配置文件/org/apache/catalina/startup/catalina.properties中指定的类库支持

Catalina和Shared都从Common中继承,包含的类库也在上面配置文件中指定。

WebappX在部署单个Tomcat5实例时指定。一个webapp下面的类库对另外一个是不可见的

Tomcat加载类的顺序和普通的不太一样,如下:

Bootstrap classes of your JVM

System class loader classses (described above)

/WEB-INF/classes of your web application

/WEB-INF/lib/*.jar of your web application

$CATALINA_HOME/common/classes

$CATALINA_HOME/common/endorsed/*.jar

$CATALINA_HOME/common/lib/*.jar

$CATALINA_BASE/shared/classes

$CATALINA_BASE/shared/lib/*.jar

注意,如果希望不使用JVM本身提供的类。这时可以使用jdk的endorsed特性

3. Catalina类的作用

如果要启动Tomcat,那么一个org.apache.catalina.startup.Catalina实例就生成,由它完成接下来的工作。

下面是它启动的代码

Java代码


public void start() {

if (getServer() == null) {
load();
}

if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}

long t1 = System.nanoTime();

// Start the new server
if (getServer() instanceof Lifecycle) {
try {
((Lifecycle) getServer()).start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
}

long t2 = System.nanoTime();
if(log.isInfoEnabled())
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");

try {
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}

if (await) {
await();
stop();
}

}

启动过程先载入配置文件,然后根据配置文件启动的Server实例启动实例

4. Server对象的生成

服务的生成就是根据配置文件server.xml,实例化的对象。对象实例化过程中,会做载入webapp,在特定端口等待客户连接等工作。

从server.xml到对象的映射是通过commons-digester.jar包完成的。这个包的一个主要功能就是映射xml到java对象。

catalina类的方法createStartDigester完成了这个工作。部分代码如下

Java代码


protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);

------------- ------------- -------------
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
————————————————————————————
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Host/Cluster/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Cont

——————————————————
return (digester);
}

Java代码


digester会在解析之后,返回一个对象。

5. 服务的中止

org.apache.catalina.core.StandardServer.await是保持tomcat运行的秘密。方法启动一个ServerSocket,侦听发出停止的字符串。这是一个死循环。当有停止运行的字符发出,跳出此循环。

Java代码


public void await() {
// Negative values - don't wait on port - tomcat is embedded or we just don't like ports
if( port == -2 ) {
// undocumented yet - for embedding apps that are around, alive.
return;
}
if( port==-1 ) {
while( true ) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
}
if( stopAwait ) return;
}
}

// Set up a server socket to wait on
ServerSocket serverSocket = null;
try {
serverSocket =
new ServerSocket(port, 1,
InetAddress.getByName("localhost"));
} catch (IOException e) {
log.error("StandardServer.await: create[" + port
+ "]: ", e);
System.exit(1);
}

// Loop waiting for a connection and a valid command
while (true) {

// Wait for the next connection
Socket socket = null;
InputStream stream = null;
try {
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000); // Ten seconds
stream = socket.getInputStream();
} catch (AccessControlException ace) {
log.warn("StandardServer.accept security exception: "
+ ace.getMessage(), ace);
continue;
} catch (IOException e) {
log.error("StandardServer.await: accept: ", e);
System.exit(1);
}

// Read a set of characters from the socket
StringBuffer command = new StringBuffer();
int expected = 1024; // Cut off to avoid DoS attack
while (expected < shutdown.length()) {
if (random == null)
random = new Random();
expected += (random.nextInt() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (IOException e) {
log.warn("StandardServer.await: read: ", e);
ch = -1;
}
if (ch < 32) // Control character or EOF terminates loop
break;
command.append((char) ch);
expected--;
}

// Close the socket now that we are done with it
try {
socket.close();
} catch (IOException e) {
;
}

// Match against our command string
boolean match = command.toString().equals(shutdown);
if (match) {
break;
} else
log.warn("StandardServer.await: Invalid command '" +
command.toString() + "' received");

}

// Close the server socket and return
try {
serverSocket.close();
} catch (IOException e) {
;
}

}

跳出循环后,系统执行关闭连接等资源的操作,服务就中止了。

Catalina.stopServer方法用于发出一个让服务停止的指令

Java代码


Socket socket = new Socket(hostAddress, getServer().getPort());
OutputStream stream = socket.getOutputStream();
String shutdown = getServer().getShutdown();
for (int i = 0; i < shutdown.length(); i++)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: