从Tomcat的启动脚本中可以看出Tomcat的入口是Bootstrap类的main方法。
Tomcat启动分为初始化init和启动start两个阶段。我们先分析初始化init阶段。
1.Bootstrap类
(1)main方法
public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
//创建了catalina对象
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to prevent
// a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
//执行这里的方法
//调用catalina对象await方法
daemon.setAwait(true);
//调用catalina对象load方法
daemon.load(args);
//调用catalina对象start方法
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
Bootstrap类初始化:
- bootstrap.init:创建Catalina对象
- daemon.setAwait:调用catalina对象await方法
- daemon.load:调用catalina对象load方法
(2)init方法
public void init() throws Exception {
log.info("Bootstrap--------init()");
//初始化class加载器
//tomcat有三个class类加载器:commonLoader、catalinaLoader、sharedLoader
//在这里catalinaLoader、sharedLoader指向的是commonLoader引用,三个对象其实相同的
//在tomcat6之前的版本三个还是不同
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
//使用类加载器加载Catalina类
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
//实例化Catalina对象
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
(3)setAwait方法
public void setAwait(boolean await)
throws Exception {
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Boolean.TYPE;
Object paramValues[] = new Object[1];
paramValues[0] = Boolean.valueOf(await);
Method method =
catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
//调用catalina对象await方法
//设置catalina对象的await属性为true
method.invoke(catalinaDaemon, paramValues);
}
(4)load方法
private void load(String[] arguments)
throws Exception {
log.info("Bootstrap--------load()");
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
//调用catalina对象load方法
method.invoke(catalinaDaemon, param);
}
2.Catalina类
(1)setAwait
public void setAwait(boolean b) {
await = b;
}
就是将catalina对象的await属性设置为true,用来在容器启动后,由于main函数结束导致进程直接结束。
(2)load
public void load() {
log.info("Catalina--------load()");
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
//创建Digester对象,用来解析server.xml文件
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
try {
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try {
inputSource.setByteStream(inputStream);
digester.push(this);
//解析server.xml文件,创建Server、service、Engine、Connector等对象
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
try {
//调用StandardServer的init方法
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
Catalina类在tomcat初始化时:
- createStartDigester:创建Digester对象,用来解析xml文件
- digester.parse:解析server.xml文件,创建Server、service、Engine、Connector等对象
- getServer().init:开始初始化上述创建的对象。当然初始化是有顺序的,先创建父元素再创建子元素
3.StandardServer类init方法
Tomcat的server默认实现是StandardServer。StandardServer继承了LifecycleBase。LifecycleBase类实现了Lifecycle接口。Lifecycle接口是用来管理生命周期。Tomcat的server、service、engine都实现了Lifecycle接口。方便统一管理生命周期。
StandardServer的init方法实际上是LifecycleBase的方法。
(1)init
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
//设置对象状态为before_init
setStateInternal(LifecycleState.INITIALIZING, null, false);
//调用子类实现的方法
initInternal();
//设置对象状态为after_init
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
(2)initInternal方法
protected void initInternal() throws LifecycleException {
log.info("StandardServer--------init()");
super.initInternal();
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
//调用NamingResourcesImpl的init方法
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
//调用StandardService的init方法
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
Server的初始化:
- setStateInternal:设置Server的状态为before_init
- initInternal:向MBeanFactory注册,调用service的初始化方法
- setStateInternal:设置Server的状态为after_init
4.StandardService类init方法
Tomcat的service默认实现是StandardService。同样这里的init方法,也是LifecycleBase的init方法。StandardService初始化本质上还是调用自身的initInternal方法。
(1)initInternal
protected void initInternal() throws LifecycleException {
log.info("StandardService--------init()");
super.initInternal();
if (container != null) {
//调用StandardEngine的init方法
container.init();
}
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
//执行线程池的init方法,将executor注册给JMX服务器
executor.init();
}
// Initialize mapper listener
mapperListener.init();
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
//调用Connector的init方法
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
Service的初始化:
- setStateInternal:设置Service的状态为before_init
- initInternal:顺序调用Engine的init方法,线程池executor的init方法,Connector的init方法
- setStateInternal:设置Server的状态为after_init
5.Engine类init方法
Tomcat的Engine默认实现是StandardEngine。同理,init方法又会调用Engine的initInternal方法。
(1)initInternal方法
protected void initInternal() throws LifecycleException {
getRealm();
super.initInternal();
}
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
//创建线程池
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
Engine的初始化:
- setStateInternal:设置Engine的状态为before_init
- initInternal:创建线程池
- setStateInternal:设置Engine的状态为after_init
6.Connector类init方法
同样,init方法会执行initInternal方法,不过这个initInternal方法是Connector本身的方法。
(1)initInternal
protected void initInternal() throws LifecycleException {
super.initInternal();
//创建CoyoteAdapter
adapter = new CoyoteAdapter(this);
//创建ProtocolHandler,bio是Http11Protocol,nio是Http11NioProtocol
protocolHandler.setAdapter(adapter);
// 默认值
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
try {
//调用protocolHandler的init方法
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
Connector的初始化:
- setStateInternal:设置Connector的状态为before_init
- initInternal:创建CoyoteAdapter和ProtocolHandler,调用protocolHandler的init方法
- setStateInternal:设置Connector的状态为after_init
(2)Connector内部重要属性说明
Connector内部有两个重要属性:ProtocolHandler和Adapter。
ProtocolHandler:
ProtocolHandler是协议的处理类,不同的协议有不同的处理逻辑,Connector根据配置的不同实例化不同的ProtocolHandler。
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.Http11NioProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpNioProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
Adapter:
Adapter的实现时CoyoteAdapter,完成request的封装。
7.ProtocolHandler类init方法
ProtocolHandler是一个协议处理的接口。不同的协议有不同的实现,BIO为Http11Protocol,NiO为Http11NioProtocol。
Http11Protocol包含了Http11ConnectionHandler和JIoEndpoint两个对象。
Http11ConnectionHandler:
维护了Http11Processor对象池,调用CoyoteAdapter完成request的封装。
JIoEndpoint:
维护Acceptor和Worker两个线程池。
Http11NioProtocol包含了Http11ConnectionHandler和NioEndpoint两个对象。
以NIO为例,说明下Acceptor和Poller、worker作用。
Acceptor:
在NIO中,Acceptor是接收socket线程,获得SocketChannel对象并封装成NioChannel。然后再将NioChannel封装成PollerEvent对象并放入events queue里。
Poller:
Poller线程中维护了一个Selector对象(主Selector),在connector中并不止一个Selector,在socket的读写数据时,为了控制timeout也有一个Selector。
Poller是NIO实现的主要线程。首先从events queue中取出PollerEvent对象,然后将对象中的channel以OP_READ事件注册到主Selector中,然后主Selector执行select操作,遍历出可以读数据的socket,并从Worker线程池中拿到可用的Worker线程去执行。
Worker:
Worker线程拿到Poller传过来的socket后,将socket封装在SocketProcessor对象中。然后从Http11NioProcessor对象中调用CoyoteAdapter将从socket中读取的httprequest封装成HttpServletRequest对象,并分派到相应的servlet处理,最后将response通过socket发回client。
这里我们以Http11NioProtocol为例说明ProtocolHandler的初始化。Http11NioProtocol的init方法调用的是其父类AbstractHttp11JsseProtocol的init方法。
public void init() throws Exception {
// SSL implementation needs to be in place before end point is
// initialized
sslImplementation = SSLImplementation.getInstance(sslImplementationName);
super.init();
}
super.init()调用的是其父类AbstractProtocol的init方法。
public void init() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.init",
getName()));
if (oname == null) {
// Component not pre-registered so register it
oname = createObjectName();
if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname,
null);
}
}
if (this.domain != null) {
try {
tpOname = new ObjectName(domain + ":" +
"type=ThreadPool,name=" + getName());
Registry.getRegistry(null, null).registerComponent(endpoint,
tpOname, null);
} catch (Exception e) {
getLog().error(sm.getString(
"abstractProtocolHandler.mbeanRegistrationFailed",
tpOname, getName()), e);
}
rgOname=new ObjectName(domain +
":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null );
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
try {
//调用NioEndpoint的init方法
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex);
throw ex;
}
}
8.NioEndpoint类init方法
public final void init() throws Exception {
testServerCipherSuitesOrderSupport();
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
public void bind() throws Exception {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
//绑定接口监听
serverSock.socket().bind(addr,getBacklog());
serverSock.configureBlocking(true); //mimic APR behavior
serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
//限制最大连接数
stopLatch = new CountDownLatch(pollerThreadCount);
// Initialize SSL if needed
if (isSSLEnabled()) {
SSLUtil sslUtil = handler.getSslImplementation().getSSLUtil(this);
sslContext = sslUtil.createSSLContext();
sslContext.init(wrap(sslUtil.getKeyManagers()),
sslUtil.getTrustManagers(), null);
SSLSessionContext sessionContext =
sslContext.getServerSessionContext();
if (sessionContext != null) {
sslUtil.configureSessionContext(sessionContext);
}
// Determine which cipher suites and protocols to enable
enabledCiphers = sslUtil.getEnableableCiphers(sslContext);
enabledProtocols = sslUtil.getEnableableProtocols(sslContext);
}
if (oomParachute>0) reclaimParachute(true);
//调用NioSelectorPool的open方法
selectorPool.open();
}
9.NioSelectorPool类open方法
(1)open方法
public void open() throws IOException {
enabled = true;
getSharedSelector();
if (SHARED) {
blockingSelector = new NioBlockingSelector();
blockingSelector.open(getSharedSelector());
}
}
这里的blockingSelector的open方法是NioBlockingSelector的open方法。
public void open(Selector selector) {
sharedSelector = selector;
poller = new BlockPoller();
poller.selector = sharedSelector;
poller.setDaemon(true);
poller.setName("NioBlockingSelector.BlockPoller-"+(++threadCounter));
poller.start();
}
这里的poller是个线程,看下它的run方法。
(2)run方法
public void run() {
while (run) {
try {
//将pollerEvent中的每个socketChannel注册到selector中
events();
int keyCount = 0;
try {
int i = wakeupCounter.get();
if (i>0)
//非阻塞的 select
keyCount = selector.selectNow();
else {
//阻塞selector,直到有准备就绪的socket
wakeupCounter.set(-1);
keyCount = selector.select(1000);
}
wakeupCounter.set(0);
if (!run) break;
}catch ( NullPointerException x ) {
//sun bug 5076772 on windows JDK 1.5
if (selector==null) throw x;
if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x);
continue;
} catch ( CancelledKeyException x ) {
//sun bug 5076772 on windows JDK 1.5
if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x);
continue;
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
// 遍历就绪的socket
while (run && iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
KeyAttachment attachment = (KeyAttachment)sk.attachment();
try {
attachment.access();
iterator.remove();
sk.interestOps(sk.interestOps() & (~sk.readyOps()));
// 可读或可写时减少对应闭锁的值,此时阻塞在 NioBlockingSelector.read() 上的线程继续执行读取
if ( sk.isReadable() ) {
countDown(attachment.getReadLatch());
}
if (sk.isWritable()) {
countDown(attachment.getWriteLatch());
}
}catch (CancelledKeyException ckx) {
sk.cancel();
countDown(attachment.getReadLatch());
countDown(attachment.getWriteLatch());
}
}//while
}catch ( Throwable t ) {
log.error("",t);
}
}
events.clear();
// If using a shared selector, the NioSelectorPool will also try and
// close the selector. Try and avoid the ClosedSelectorException
// although because multiple threads are involved there is always
// the possibility of an Exception here.
if (selector.isOpen()) {
try {
// Cancels all remaining keys
selector.selectNow();
}catch( Exception ignore ) {
if (log.isDebugEnabled())log.debug("",ignore);
}
}
try {
selector.close();
}catch( Exception ignore ) {
if (log.isDebugEnabled())log.debug("",ignore);
}
}
(3)events方法
public boolean events() {
boolean result = false;
Runnable r = null;
result = (events.size() > 0);
//将pollerEvent中的每个socketChannel注册到selector中
while ( (r = events.poll()) != null ) {
r.run();
result = true;
}
return result;
}
到这里整个初始化过程就基本结束了。但是不是有点疑惑,怎么没看到Host、Context、Valve的初始化,这个在后面start部分会分析到。
10.总结
tomcat初始化过程:
- 创建Catalina对象,调用其load方法创建Digester解析server.xml,调用Server的初始化方法
- Server循环调用内部Service的初始化方法
- Service顺序调用内部的Engine、Connector初始化方法
- Connector初始化会根据协议创建ProtocolHandler和CoyoteAdapter,然后调用ProtocolHandler的初始化方法
- ProtocolHandler的初始化会调用Endpoint的初始化方法
- Endpoint初始化方法会根据协议的不同调用不同Endpoint的bind方法。BIO调用的是JIoEndpoint的bind方法,设置最大连接,创建ServerSocket监听连接。NIO调用的是NioEndpoint的bind方法,创建ServerSocket监听连接,调用NioSelectorPool的open方法获得NioBlockingSelect,然后调用
它的open方法。 - NioBlockingSelect的open方法开启poller线程,从event队列中获取连接,注册到Selector上等待读写请求。
最后看一张各组件结构图。