05-tomcat的容器初始化过程
介绍容器初始化过程之前,让我们先来看下tomcat容器的类继承关系图。
Lifecycle:顶级接口:定义了添加,删除,查找生命周期监听器的方法,以及init,stop等。
MBeanRegistration:顶级接口,定义一些注册jmx其后的处理方法。
LifecycleBase: 成员LifecycleSupport作用是发布生命周期事件。定义了init,start,stop,destroy等方法的模板结构,这里主要是切换LifecycleState的状态,子类无需再自己考虑状态切换。只需要在xxxInternal()中实现自己的相关业务即可。
LifecycleMBeanBase: 在LifecycleBase的基础上添加了MBean的注册和注销逻辑,子类无需在考虑这些。
所以,我们大部分情况下只需要阅读子类的xxxInternal方法理解容器初始化过程。
TOMCAT容器初始化过程:
1. 从04-tomcat的server.xml解析中可以知道,catalina的load()加载中getServer().init();实际上就是StandardServer,上面也说了我们其实主要只需要看xxxInternal方法。StandardServer.initInternal()方法如下:
protected void initInternal() throws LifecycleException {
super.initInternal();
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
2. StandardService.initInternal()
protected void initInternal() throws LifecycleException {
super.initInternal();
if (container != null) {
container.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof LifecycleMBeanBase) {
((LifecycleMBeanBase) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize our defined Connectors
synchronized (connectors) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
}
}
}
}
3. 从server.xml解析中可以知道container就是StandardEngine。StandardEngine.initInternal()。
StandardEngine.initInternal()没有具体做什么只是调用的父类ContainerBase.initInternal()。
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue =
new LinkedBlockingQueue<Runnable>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-")); //创建一个线程池,默认核心和最大线程数都是1.
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
4. executor.init(); 默认情况下没有executor。
5. connector.init();
这里以常用的8080端口为例,先看server.xml里面的配置:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
通过ConnectorCreateRule.begin()解析上面的节点如下:
Connector con = new Connector(attributes.getValue("protocol"));
public Connector(String protocol) {
setProtocol(protocol);
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
}
}
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);
}
}
}
判断APR是否可用,关于APR具体描述请参考这个官方文档介绍Apache Portable Runtime (APR) based Native library for Tomcat。
因为这里没有开启APR,所以protocolHandlerClassName = org.apache.coyote.http11.Http11Protocol。因此在Connector的构造方法里面会创建一个Http11Protocol的protocolHandler。
public Http11Protocol() {
endpoint = new JIoEndpoint(); //创建一个JIoEndpoint
cHandler = new Http11ConnectionHandler(this); //创建Http11ConnectionHandler
((JIoEndpoint) endpoint).setHandler(cHandler); //把cHandler 赋值给endPoint
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
在通过SetAllPropertiesRule把后面的属性设置给Connector。
public boolean setProperty(String name, String value) {
String repl = name;
if (replacements.get(name) != null) {
repl = replacements.get(name);
}
return IntrospectionUtils.setProperty(protocolHandler, repl, value);
}
可以看到最后都设置给了protocolHandler。
看完了这些,让我们继续看connector.initInternal()方法:
protected void initInternal() throws LifecycleException {
super.initInternal();
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter); //设置protocolHandler的adapter
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
try {
protocolHandler.init(); //protocolHandler初始化
} catch (Exception e) {
}
mapperListener.init(); //mapperListener初始化。
}
6. protocolHandler.init(); ==》 AbstractProtocol.init() ==》 JIoEndpoint.init() ==> AbstractEndpoint.init()
public void init() throws Exception { //AbstractProtocol.init()
。。。
try {
endpoint.init(); //endpoint就是上面创建的JIoEndpoint
} catch (Exception ex) {
}
}
public final void init() throws Exception { //AbstractEndpoint.init()
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
7. JIoEndpoint.bind()
public void bind() throws Exception {
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1; //设置acceptorThreadCount =1
}
if (getMaxConnections() == 0) {
setMaxConnections(getMaxThreadsExecutor(true)); //因为我们的server.xml里面没有配置executor。所以这里取得默认值 private int maxThreads = 200.
}
if (serverSocketFactory == null) {
if (isSSLEnabled()) {
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
serverSocketFactory = new DefaultServerSocketFactory(this); //创建serverSocket工厂
}
}
if (serverSocket == null) {
try {
if (getAddress() == null) {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog()); //创建serversocket,实际调用的new ServerSocket (port, backlog)
} else {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog(), getAddress());
}
} catch (BindException orig) {
}
}
}
总结:
容器的初始化是从父容器依次向子容器进行的。
容器的jmx注册和注销等过程都抽取到父类中,子类无需再重复这些逻辑。
protocolHandler也就是本例中的http11protocol的属性值可以通过IntrospectionUtils.setProperty(protocolHandler, repl, value)设置,也就是通过在server.xml对应的Connector节点中指明相应的属性。