Tomcat 源码分析(二)-请求分析(1)
一.处理线程的产生
了解一下大体的线程情况
在默认配置下的Tomcat启动之后,后有一下的线程。其中1个用户线程,剩下5个为守护线程(如下图所示)。
接下来,要说明的就是这个http-bio-8080
开头的两个守护线程(即 http-bio-8080-Acceptor-0 和 http-bio-8080-AsyncTimeout )
初始化各个必要的对象
加载配置信息,创建Connector节点
默认的这个线程的配置在server.xml
文件中 :【对应节点 Server/Service/Connector 节点 】
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
**所以,首先Tomcat 容器启动的时候 Digester 会读取这个配置文件,**产生相应的组件对象并采取链式调用的方式调用它们的 init 和 start 方法 。
connector 节点时是这么处理配置是这样的:
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
这里创建节点的时候会调用规则类ConnectorCreateRule,这里ConnectorCreateRule.begin()方法会被调用,用以创建Connector 节点 。
ConnectorCreateRule.begin()方法如下:
@Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Service svc = (Service)digester.peek();
Executor ex = null;
if ( attributes.getValue("executor")!=null ) {
ex = svc.getExecutor(attributes.getValue("executor"));
}
//这里节点protocol的配置作为参数(HTTP/1.1)去创建Connector节点对象
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
Connector节点构造方法,创建Http11Protocol
构造方法传入参数 :HTTP/1.1
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
其中,这里会先执行Connector .setProtocol方法,参数为HTTP/1.1
。方法代码如下:
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) { //这个值默认为false的样子
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);
}
}
}
然后,Connector 类实例变量 protocolHandlerClassName 值设置为org.apache.coyote.http11.Http11Protocol
,构造方法接下来的就是使用反射来产生Http11Protocol 的对象:
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
Http11Protocol 对象的产生
Http11Protocol 的构造方法:产生了一个JIoEndpoint对象
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
这里,产生了一个JIoEndpoint对象。
到这里,所有的构造方法调用结束,该产生的对象也已经创建好了。
大概图解一下构造的调用的过程:
执行start方法
按照Tomcat 的启动方式,在创建完节点对象时候,会链式的调用start方法到具体实现类的startInternal()方法
Connector 类的 startInternal 方法
先看源码:
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
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.start();
}
这里可以知道,调用的是protocolHandler.start();
,然而,这个protocolHandler在其构造方法中就被赋值Http11Protocol 对象了。
所以,这里调用的是Http11Protocol 类的 star() 方法
这里start方法在父类org.apache.coyote.AbstractProtocol 中
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
endpoint.start();//这里,这里,又调用了一个start方法
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
这里会调用endpoint的start()方法。
endpoint 是org.apache.tomcat.util.net.JIoEndpoint
类的实例 。
最终会执行该类的 JIoEndpoint.startInternal 方法
这里,就是产生开头说明的两个线程的地方了,(ノ`Д)ノ调用了这么久终于到地方了。
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
startAcceptorThreads(); //这个启动http-bio-8080-Acceptor-0 线程
// Start async timeout thread
//启动 http-bio-8080-AsyncTimeout 线程
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
startAcceptorThreads()方法如下:启动http-bio-8080-Acceptor-0 线程
这个方法在AbstractEndpoint中,而createAcceptor() 方法则在实现类JIoEndpoint中;
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
/**
* Hook to allow Endpoints to provide a specific Acceptor implementation.
*/
protected abstract Acceptor createAcceptor();
结束,开启线程了
到这里,线程已经启动了,然而,具体线程的操作呢,这个在最后说明的,JIoEndpoint类的实现方法createAcceptor()中。这个接下来再具体学习一下。
(对了,ajp-bio-8009-Acceptor-0 和 ajp-bio-8009-AsyncTimeout 两个守护线程的产生和启动方式也差不多这样子╮(╯_╰)╭ )
先来试试画一个,启动线程各个类的调用图,丫的(ノ`Д)ノ真的是很乱
参考资料
小杭 ヾ(◍°∇°◍)ノ゙
2019-01-08