【转】Tomcat启动源代码分析

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方法,核心代码如下:


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 }

主要是解析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);


接下来就是初始化并启动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);
}
}
}

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));
}
}

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;
}

初始化后整个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();
}

}

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();
}

}


下面一幅图有助理解,Tomcat请求客户端情况的具体分析后面再分析。


http://www.blogjava.net/persister/archive/2009/06/17/282875.html
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值