Tomcat的源码研读怎么说都是一件恐怖的事情,代码太多!不过管中窥豹,也可偶尔为之。谁让我好读书,不求甚解呢。前面对嵌入式Tomcat(Tomcat 4.0)进行了一个简单的分析,见 在应用中使用嵌入式Tomcat。
今天的这篇文章对Tomcat的源码进行了一些分析,主要是Tomcat启动。
1、Bootstrap.java。正如OFBiz的启动程序时Start.java,Tomcat的启动程序是Bootstrap.java。OFBiz的Start.java做的事情最主要的就是加载相应的lib库和启动tomcat及其component。Tomcat的Bootstrap也是加载相应的lib库和启动Catalina
的process方法。Lib库放置在common,server和share目录中。
2、Catalina.java。这个类主要研究start方法,核心代码如下:
主要是解析server.xml文件,采用的 Digester,非常著名的xml解析器。通过解析的源代码分析,Tomcat将Engine,Host和Context当成Container,这是一个虚拟的概念,具体的容器都实现了Container, Lifecycle接口。所以Service不是直接引用一个Engine,而是一个Container。 在应用中使用嵌入式Tomcat的tomcat实现结构图中都是相邻层次之间都是双向引用的,如Service引用上面的Server还有下级的Connector和Container。大致类图如下:
Digester解析server.xml就是将这些实例初始化并且配置好相互的引用关系。Connector通过Container的invoke方法将接受到了请求交给Container最高层Engine处理:
接下来就是初始化并启动server:
server的初始化最终就是将connector进行初始化:
Protocol Handler初始化会初始化PoolTcpEndPoint,这个时候就会启动Tomcat的端口了:
初始化后整个tomcat就做好准备启动,这时还不能处理客户端的请求,必须启动相关容器。与初始化相比,启动的时候同时启动Container和Connector。Container的启动会将Engine,Host和Conext都启动起来。
Connector启动的时候就会启动PoolTcpEndPoint,看看它的run方法就大概知道怎么回事了:
MasterSlaveWorkerThread默认最大线程数是20,Tomcat优化时可以设置此线程数,见 Tomcat优化方案 。
MasterSlaveWorkerThread使用的是Guarded Suspension Pattern,如果有新的Socket分配,那么进行处理,available作为警戒条件:
下面一幅图有助理解,Tomcat请求客户端情况的具体分析后面再分析。
今天的这篇文章对Tomcat的源码进行了一些分析,主要是Tomcat启动。
1、Bootstrap.java。正如OFBiz的启动程序时Start.java,Tomcat的启动程序是Bootstrap.java。OFBiz的Start.java做的事情最主要的就是加载相应的lib库和启动tomcat及其component。Tomcat的Bootstrap也是加载相应的lib库和启动Catalina
的process方法。Lib库放置在common,server和share目录中。
2、Catalina.java。这个类主要研究start方法,核心代码如下:
1
Digester digester
=
createStartDigester();
2 File file = configFile();
3 try {
4 InputSource is =
5 new InputSource( " file:// " + file.getAbsolutePath());
6 FileInputStream fis = new FileInputStream(file);
7 is.setByteStream(fis);
8 digester.push( this );
9 digester.parse(is);
10 fis.close();
11 } catch (Exception e) {
12 System.out.println( " Catalina.start using "
13 + configFile() + " : " + e);
14 e.printStackTrace(System.out);
15 System.exit( 1 );
16 }
2 File file = configFile();
3 try {
4 InputSource is =
5 new InputSource( " file:// " + file.getAbsolutePath());
6 FileInputStream fis = new FileInputStream(file);
7 is.setByteStream(fis);
8 digester.push( this );
9 digester.parse(is);
10 fis.close();
11 } catch (Exception e) {
12 System.out.println( " Catalina.start using "
13 + configFile() + " : " + e);
14 e.printStackTrace(System.out);
15 System.exit( 1 );
16 }
主要是解析server.xml文件,采用的 Digester,非常著名的xml解析器。通过解析的源代码分析,Tomcat将Engine,Host和Context当成Container,这是一个虚拟的概念,具体的容器都实现了Container, Lifecycle接口。所以Service不是直接引用一个Engine,而是一个Container。 在应用中使用嵌入式Tomcat的tomcat实现结构图中都是相邻层次之间都是双向引用的,如Service引用上面的Server还有下级的Connector和Container。大致类图如下:
Digester解析server.xml就是将这些实例初始化并且配置好相互的引用关系。Connector通过Container的invoke方法将接受到了请求交给Container最高层Engine处理:
CoyoteAdapter.java
connector.getContainer().invoke(request, response);
connector.getContainer().invoke(request, response);
接下来就是初始化并启动server:
//
Start the new server
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Wait for the server to be told to shut down
server.await();
} catch (LifecycleException e) {
System.out.println( " Catalina.start: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null ) {
System.out.println( " ----- Root Cause ----- " );
e.getThrowable().printStackTrace(System.out);
}
}
}
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Wait for the server to be told to shut down
server.await();
} catch (LifecycleException e) {
System.out.println( " Catalina.start: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null ) {
System.out.println( " ----- Root Cause ----- " );
e.getThrowable().printStackTrace(System.out);
}
}
}
server的初始化最终就是将connector进行初始化:
CoyoteConnector.java
/**
* Initialize this connector (create ServerSocket here!)
*/
public void initialize()
throws LifecycleException {
if (initialized)
throw new LifecycleException
(sm.getString( " coyoteConnector.alreadyInitialized " ));
this .initialized = true ;
if ( oname == null && (container instanceof StandardEngine)) {
try {
// we are loaded directly, via API - and no name was given to us
StandardEngine cb = (StandardEngine)container;
oname = createObjectName(cb.getName(), " Connector " );
Registry.getRegistry( null , null )
.registerComponent( this , oname, null );
} catch (Exception e) {
log ( " Error registering connector " + e.toString());
}
if (debug > 0 )
log( " Creating name for connector " + oname);
}
// Initialize adapter
adapter = new CoyoteAdapter( this );
protocolHandler.setAdapter(adapter);
IntrospectionUtils.setProperty(protocolHandler, " jkHome " ,
System.getProperty( " catalina.base " ));
// Configure secure socket factory
if (factory instanceof CoyoteServerSocketFactory) {
IntrospectionUtils.setProperty(protocolHandler, " secure " ,
"" + true );
CoyoteServerSocketFactory ssf =
(CoyoteServerSocketFactory) factory;
IntrospectionUtils.setProperty(protocolHandler, " algorithm " ,
ssf.getAlgorithm());
IntrospectionUtils.setProperty(protocolHandler, " ciphers " ,
ssf.getCiphers());
IntrospectionUtils.setProperty(protocolHandler, " clientauth " ,
ssf.getClientAuth());
IntrospectionUtils.setProperty(protocolHandler, " keystore " ,
ssf.getKeystoreFile());
IntrospectionUtils.setProperty(protocolHandler, " randomfile " ,
ssf.getRandomFile());
IntrospectionUtils.setProperty(protocolHandler, " rootfile " ,
ssf.getRootFile());
IntrospectionUtils.setProperty(protocolHandler, " keypass " ,
ssf.getKeystorePass());
IntrospectionUtils.setProperty(protocolHandler, " keytype " ,
ssf.getKeystoreType());
IntrospectionUtils.setProperty(protocolHandler, " protocol " ,
ssf.getProtocol());
IntrospectionUtils.setProperty(protocolHandler,
" sSLImplementation " ,
ssf.getSSLImplementation());
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
( " coyoteConnector.protocolHandlerInitializationFailed " , e));
}
}
/**
* Initialize this connector (create ServerSocket here!)
*/
public void initialize()
throws LifecycleException {
if (initialized)
throw new LifecycleException
(sm.getString( " coyoteConnector.alreadyInitialized " ));
this .initialized = true ;
if ( oname == null && (container instanceof StandardEngine)) {
try {
// we are loaded directly, via API - and no name was given to us
StandardEngine cb = (StandardEngine)container;
oname = createObjectName(cb.getName(), " Connector " );
Registry.getRegistry( null , null )
.registerComponent( this , oname, null );
} catch (Exception e) {
log ( " Error registering connector " + e.toString());
}
if (debug > 0 )
log( " Creating name for connector " + oname);
}
// Initialize adapter
adapter = new CoyoteAdapter( this );
protocolHandler.setAdapter(adapter);
IntrospectionUtils.setProperty(protocolHandler, " jkHome " ,
System.getProperty( " catalina.base " ));
// Configure secure socket factory
if (factory instanceof CoyoteServerSocketFactory) {
IntrospectionUtils.setProperty(protocolHandler, " secure " ,
"" + true );
CoyoteServerSocketFactory ssf =
(CoyoteServerSocketFactory) factory;
IntrospectionUtils.setProperty(protocolHandler, " algorithm " ,
ssf.getAlgorithm());
IntrospectionUtils.setProperty(protocolHandler, " ciphers " ,
ssf.getCiphers());
IntrospectionUtils.setProperty(protocolHandler, " clientauth " ,
ssf.getClientAuth());
IntrospectionUtils.setProperty(protocolHandler, " keystore " ,
ssf.getKeystoreFile());
IntrospectionUtils.setProperty(protocolHandler, " randomfile " ,
ssf.getRandomFile());
IntrospectionUtils.setProperty(protocolHandler, " rootfile " ,
ssf.getRootFile());
IntrospectionUtils.setProperty(protocolHandler, " keypass " ,
ssf.getKeystorePass());
IntrospectionUtils.setProperty(protocolHandler, " keytype " ,
ssf.getKeystoreType());
IntrospectionUtils.setProperty(protocolHandler, " protocol " ,
ssf.getProtocol());
IntrospectionUtils.setProperty(protocolHandler,
" sSLImplementation " ,
ssf.getSSLImplementation());
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
( " coyoteConnector.protocolHandlerInitializationFailed " , e));
}
}
Protocol Handler初始化会初始化PoolTcpEndPoint,这个时候就会启动Tomcat的端口了:
public
void
initEndpoint()
throws
IOException, InstantiationException {
try {
if (factory == null )
factory = ServerSocketFactory.getDefault();
if (serverSocket == null ) {
try {
if (inet == null ) {
serverSocket = factory.createSocket(port, backlog);
} else {
serverSocket = factory.createSocket(port, backlog, inet);
}
} catch ( BindException be ) {
throw new BindException(be.getMessage() + " : " + port);
}
}
if ( serverTimeout >= 0 )
serverSocket.setSoTimeout( serverTimeout );
} catch ( IOException ex ) {
throw ex;
} catch ( InstantiationException ex1 ) {
throw ex1;
}
initialized = true ;
}
try {
if (factory == null )
factory = ServerSocketFactory.getDefault();
if (serverSocket == null ) {
try {
if (inet == null ) {
serverSocket = factory.createSocket(port, backlog);
} else {
serverSocket = factory.createSocket(port, backlog, inet);
}
} catch ( BindException be ) {
throw new BindException(be.getMessage() + " : " + port);
}
}
if ( serverTimeout >= 0 )
serverSocket.setSoTimeout( serverTimeout );
} catch ( IOException ex ) {
throw ex;
} catch ( InstantiationException ex1 ) {
throw ex1;
}
initialized = true ;
}
初始化后整个tomcat就做好准备启动,这时还不能处理客户端的请求,必须启动相关容器。与初始化相比,启动的时候同时启动Container和Connector。Container的启动会将Engine,Host和Conext都启动起来。
Connector启动的时候就会启动PoolTcpEndPoint,看看它的run方法就大概知道怎么回事了:
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused) {
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
// Ignore
}
}
// Allocate a new worker thread
MasterSlaveWorkerThread workerThread = createWorkerThread();
if (workerThread == null ) {
try {
// Wait a little for load to go down: as a result,
// no accept will be made until the concurrency is
// lower than the specified maxThreads, and current
// connections will wait for a little bit instead of
// failing right away.
Thread.sleep( 100 );
} catch (InterruptedException e) {
// Ignore
}
continue ;
}
// Accept the next incoming connection from the server socket
Socket socket = acceptSocket();
// Hand this socket off to an appropriate processor
workerThread.assign(socket);
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
synchronized (threadSync) {
threadSync.notifyAll();
}
}
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused) {
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
// Ignore
}
}
// Allocate a new worker thread
MasterSlaveWorkerThread workerThread = createWorkerThread();
if (workerThread == null ) {
try {
// Wait a little for load to go down: as a result,
// no accept will be made until the concurrency is
// lower than the specified maxThreads, and current
// connections will wait for a little bit instead of
// failing right away.
Thread.sleep( 100 );
} catch (InterruptedException e) {
// Ignore
}
continue ;
}
// Accept the next incoming connection from the server socket
Socket socket = acceptSocket();
// Hand this socket off to an appropriate processor
workerThread.assign(socket);
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
synchronized (threadSync) {
threadSync.notifyAll();
}
}
MasterSlaveWorkerThread默认最大线程数是20,Tomcat优化时可以设置此线程数,见 Tomcat优化方案 。
MasterSlaveWorkerThread使用的是Guarded Suspension Pattern,如果有新的Socket分配,那么进行处理,available作为警戒条件:
synchronized
void
assign(Socket socket) {
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this .socket = socket;
available = true ;
notifyAll();
}
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while ( ! available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Socket socket = this .socket;
available = false ;
notifyAll();
return (socket);
}
public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
endpoint.processSocket(socket, con, threadData);
// Finish up this request
endpoint.recycleWorkerThread(this);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this .socket = socket;
available = true ;
notifyAll();
}
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while ( ! available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Socket socket = this .socket;
available = false ;
notifyAll();
return (socket);
}
public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
endpoint.processSocket(socket, con, threadData);
// Finish up this request
endpoint.recycleWorkerThread(this);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
下面一幅图有助理解,Tomcat请求客户端情况的具体分析后面再分析。