一 概述
好久没有继续更新tomat相关文章了,今天刚好工作上没有什么事情做,就写写相关tomat的理解了。最近也看了别人写的tomcat理解,感觉都比自己写的好,不仅语言描述准确而且内容有深度干货也比较多。我就按照看代码的理解写写了,内容会比较肤浅。前面分析了tomcat在准备启动前做相关资源初始化处理,现在来分析下正在的启动处理代码。
二 tomcat启动处理
a.我这里仍然从Bootstrap类的main函数中daemon.start();入口开始分析,跟进到start()方法的代码如下:
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
首先判断catalinaDaemon属性是否为空,否则调用init()方法完成初始化,否则通过反射机制调用了catalinaDaemon对象的start方法。catalinaDaemon这个对象就是org.apache.catalina.startup.Catalina类的对象。
b.进入到org.apache.catalina.startup.Catalina类的start()方法查看代码。如下:
public void start() {
if (getServer() == null) {//判断server对象是否为空,为空就调用load初始化
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start(); //拿到server对象,调用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();
}
}
上面代码主要涉及两部操作
- 1.判断tomcat的最外层容器server对象是否已经初始化,如果没有那么调用load进行初始化处理
- 2.getServer拿到已经初始化成功的server对象调用start()方法启动服务,这里也就是整个容器启动的入口处
c.跟进server对象的start()方法,其调用的是LifecycleBase抽象类的start()方法,查看start方法中代码最终还是调用了startInternal()方法,而且发现startInternal()方法在LifecycleBase类中是抽象的方法。按抽象模版方法模式,调用startInternal()其实调用的是子类的具体实现。我们看看LifecycleBase类中的代码
public synchronized final void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) ||
LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted",
toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted",
toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
setState(LifecycleState.STARTING_PREP);
try {
startInternal(); //调用LifecycleBase类中的抽象方法startInternal方法
} catch (LifecycleException e) {
setState(LifecycleState.FAILED);
throw e;
}
if (state.equals(LifecycleState.FAILED) ||
state.equals(LifecycleState.MUST_STOP)) {
stop();
} else {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
}
setState(LifecycleState.STARTED);
}
}
下面就是LifecycleBase类中的抽象方法。
protected abstract void startInternal() throws LifecycleException;
d.前面我们介绍过server的表中实现是StandardServer类,查看该类的startInternal具体实现。代码如下:
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
// Start our defined Services
synchronized (services) {
for (int i = 0; i < services.length; i++) {
services[i].start();//循环调用service接口实现类的start方法来启动容器
}
}
}
StandardServer类中的startInternal做了一件事情,就是通过遍历Service数组循环启动service对象,调用start()方法。前面架构图中介绍过service接口的标准实现是StandardService类,所以start方法按照前面介绍的语意,仍然还是调用StandardService类中的startInternal方法。
e.查看StandardService的startInternal方法启动代码。如下:
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (container != null) {
synchronized (container) {
container.start(); //StandardEngine启动
}
}
synchronized (executors) {
for ( int i=0; i<executors.size(); i++ ) {
executors.get(i).start(); //线程池启动
}
}
// Start our defined Connectors second
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
((Lifecycle) connectors[i]).start(); //连接器启动
}
}
}
StandardService类中的startInternal方法做了3件事情
- 1.启动StandardEngine容器
- 2.循环启动线程池。没看太懂这里的线程池是做什么用的?后面有时间再研究下!
- 3.循环启动Connector数组。
f.这里我就直接跟踪tomcat启动的主流程了,其它细节的地方就不仔细看啦!立即进入到Connector类的startInternal方法
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
// Protocol handlers do not follow Lifecycle conventions.
// protocolHandler.init() needs to wait until the connector.start()
try {
protocolHandler.init(); //初始化协议处理,protocolHandler时一个接口,具体实现看具体实现
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed", e));
}
try {
protocolHandler.start(); //启动协议处理
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed", e));
}
// MapperListener doesn't follow Lifecycle conventions either
mapperListener.init();
}
g.初始化协议处理,protocolHandler属性是一个ProtocolHandler类型的,ProtocolHandler接口类的具体实现是用的什么类,我们查看Connector类的带参构造函数。代码如下:
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error
(sm.getString
("coyoteConnector.protocolHandlerInstantiationFailed", e));
}
}
可以看出Connector类在实例化的时候传入了一个字符串protocol参数。在前面章节《tomcat原理解析(三):资源初始化》介绍Connector初始化的时候,通过Connector con = new Connector(attributes.getValue("protocol"));来实例化。这里attributes.getValue("protocol")取的值实际就是在解析server.xml时的
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> protocol节点的值。通过server.xml配置文件看出在初始化Connector连接器的时候已经默认指定了协议处理器对象。到这里继续跟进setProtocol(protocol);中代码。如下:
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
以上的方法跟进传入的protocol字符串来区分使用什么类型的协议处理器类。在确定使用什么协议处理后,通过Class.forName来实例化协议处理器对象。在这里tomcat默认使用的org.apache.coyote.http11.Http11Protocol协议处理。
我们看下Http11Protocol类中init做了什么事情,代码如下:
@Override
public void init() throws Exception {
((JIoEndpoint)endpoint).setName(getName());//设置名称
((JIoEndpoint)endpoint).setHandler(cHandler);//设置连接对象处理器,在拿到socket对象后会传入处理器中
// Verify the validity of the configured socket factory
try {//根据条件初始化socketFacor
if (isSSLEnabled()) {
sslImplementation =
SSLImplementation.getInstance(sslImplementationName);
socketFactory = sslImplementation.getServerSocketFactory();
((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);
} else if (socketFactoryName != null) {
socketFactory = (ServerSocketFactory) Class.forName(socketFactoryName).newInstance();
((JIoEndpoint)endpoint).setServerSocketFactory(socketFactory);
}
} catch (Exception ex) {
log.error(sm.getString("http11protocol.socketfactory.initerror"),
ex);
throw ex;
}
if (socketFactory!=null) {
Iterator<String> attE = attributes.keySet().iterator();
while( attE.hasNext() ) {
String key = attE.next();
Object v=attributes.get(key);
socketFactory.setAttribute(key, v);
}
}
try {
endpoint.init();//JioEndpoint对象初始化处理
} catch (Exception ex) {
log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
throw ex;
}
if (log.isInfoEnabled())
log.info(sm.getString("http11protocol.init", getName()));
}
上面的初始化处理主要做了3件事情
1.设置JIoEndpoint的名称
2.设置Http11ConnectionHandler类连接对象处理
3.调用JIoEndpoint类的init方法进行初始化。
我们看看JIoEndpoint类中的inti方法中的代码,代码如下:
public void init() throws Exception { if (initialized) return; // Initialize thread count defaults for acceptor if (acceptorThreadCount == 0) { acceptorThreadCount = 1;//设置连接器中接收socket请求的线程数量 } if (serverSocketFactory == null) { serverSocketFactory = ServerSocketFactory.getDefault();//得到默认创建socket的对象,默认使用的DefaultServerSocketFactory类 } if (isSSLEnabled()) { //设置了一系列的属性,貌似跟socket的参数有关系,具体还不是非常清楚 serverSocketFactory.setAttribute(SSL_ATTR_ALGORITHM, getAlgorithm()); serverSocketFactory.setAttribute(SSL_ATTR_CLIENT_AUTH, getClientAuth()); serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_FILE, getKeystoreFile()); serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PASS, getKeystorePass()); serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_TYPE, getKeystoreType()); serverSocketFactory.setAttribute(SSL_ATTR_KEYSTORE_PROVIDER, getKeystoreProvider()); serverSocketFactory.setAttribute(SSL_ATTR_SSL_PROTOCOL, getSslProtocol()); serverSocketFactory.setAttribute(SSL_ATTR_CIPHERS, getCiphers()); serverSocketFactory.setAttribute(SSL_ATTR_KEY_ALIAS, getKeyAlias()); serverSocketFactory.setAttribute(SSL_ATTR_KEY_PASS, getKeyPass()); serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_FILE, getTruststoreFile()); serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PASS, getTruststorePass()); serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_TYPE, getTruststoreType()); serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_PROVIDER, getTruststoreProvider()); serverSocketFactory.setAttribute(SSL_ATTR_TRUSTSTORE_ALGORITHM, getTruststoreAlgorithm()); serverSocketFactory.setAttribute(SSL_ATTR_CRL_FILE, getCrlFile()); serverSocketFactory.setAttribute(SSL_ATTR_TRUST_MAX_CERT_LENGTH, getTrustMaxCertLength()); serverSocketFactory.setAttribute(SSL_ATTR_SESSION_CACHE_SIZE, getSessionCacheSize()); serverSocketFactory.setAttribute(SSL_ATTR_SESSION_TIMEOUT, getSessionTimeout()); serverSocketFactory.setAttribute(SSL_ATTR_ALLOW_UNSAFE_RENEG, getAllowUnsafeLegacyRenegotiation()); } if (serverSocket == null) { //serverSocket对象为空时即刻创建 try { if (getAddress() == null) { serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog());//根据端口号创建serverSocket对象 } else { serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog(), getAddress()); } } catch (BindException orig) { String msg; if (getAddress() == null) msg = orig.getMessage() + " <null>:" + getPort(); else msg = orig.getMessage() + " " + getAddress().toString() + ":" + getPort(); BindException be = new BindException(msg); be.initCause(orig); throw be; } } //if( serverTimeout >= 0 ) // serverSocket.setSoTimeout( serverTimeout ); initialized = true; }
到这里Http11Protocol 协议处理器初始化已经完成,intit方面主要做了3件事情如下
- 1连接器中接收socket连接的线程数量,
- 2初始化ServerSocketFactory serverSocketFactory对象,该对象默认使用的DefaultServerSocketFactory类
- 3根据端口号初始化ServerSocket对象
h.我们接着看Connector类的startInternal方法中的protocolHandler.start()的代码。代码如下
public void start() throws Exception {
if (this.domain != null) {
try {
tpOname = new ObjectName(domain + ":" + "type=ThreadPool,name=" + getName());
Registry.getRegistry(null, null).registerComponent(endpoint, tpOname, null );
} catch (Exception e) {
log.error("Can't register endpoint");
}
rgOname=new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent( cHandler.global, rgOname, null );
}
try {
endpoint.start();
} catch (Exception ex) {
log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
throw ex;
}
if (log.isInfoEnabled())
log.info(sm.getString("http11protocol.start", getName()));
}
主要就是调用了JIoEndpoint类的start()方法。继续跟进到JIoEndpoint类中的start方法代码。如下:
@Override
public void start() throws Exception {
// Initialize socket if not done before
if (!initialized) {
init();
}
if (!running) {
running = true;
paused = false;
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
// Start acceptor threads
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(getDaemon());
acceptorThread.start();
}
}
}
主要完成两件事情
- 调用JIoEndpoint类的父类AbstractEndpoint的createExecutor方法创建了一个线程池
- 循环创建接受socket请求线程,前面我们看到JIoEndpoint在init方法中初始化是设置的为一个。所以这里只创建了一个线程。新建线程里面的
Acceptor类实现了线程Runnable接口。Acceptor类也是JIoEndpoint类中的内部类。
解析分析Acceptor线程类的流程,看到这里似乎明白了tomcat客户端请求处理也是通过socket来实现的,是不是跟前面章节中的《tomcat原理解析(一):一个简单的实现》有点像。 代码如下:
protected class Acceptor implements Runnable {
/**
* 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
}
}
// Accept the next incoming connection from the server socket
try {
Socket socket = serverSocketFactory.acceptSocket(serverSocket);//通过serverSocketFacory来接收客户端socket请求,并拿到socket对象
serverSocketFactory.initSocket(socket);//socket初始化,奇怪里面怎么是空实现
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {//传入socket对象到SocketProcessor类中进行业务处理
// Close socket right away
try {
socket.close();
} catch (IOException e) {
// Ignore
}
}
}catch ( IOException x ) {
if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
} catch (Throwable t) {
log.error(sm.getString("endpoint.accept.fail"), t);
}
// The processor will recycle itself when it finishes
}
}
}
到这里整个容器已经启动完成,并开启了ServerSocket监听前端socket的请求
三 总结
在整个章节中都在描述tomcat的启动流程,其实最终目的就是要启动socket的监听,然后等待链接。对socket链接处理核心的过程都在JIoEndpoint类中,tomcat启动的过程跟初始化的处理非常相似,都是在父类LifecycleBase(具体类之前的关系图可以查看前面的《tomcat原理解析(二):整体架构》)中定义抽象的startInternal,然后由子类来具体实现,这就是使用抽象模版方法模式。所以学习和掌握设计模式能够有助于更好的理解源码。下面是我整理的一个序列图,展现了启动时的调用关系。下面章节开始分析浏览器客户端请求tomcat服务器如何找到想要的资源。