spring版本 5.x
jdk版本 1.8
spring boot 2.x
1.概述
本文将介绍下 springboot 中,tomcat启动时做了哪些事。
- 简述 tomcat结构, 主要介绍下connector 及 container
- connector 源码分析
当浏览器向服务端发起请求时,实际是 tomcat 接收到了请求,并处理了请求,所以如果我们想要完全了解浏览器的一条请求是怎样被处理的,也需要先了解下 tomcat。
2. tomcat结构简述
先盗一张官网的图
tomcat 的结构主要分为以上六个部分, 其中 connector负责处理与客户端的连接。所以分析 connector是首要任务,connect 会把接收到的请求交给其它组件处理。
3. connector 源码分析
springboot中, tomcat是在 ServletWebServerApplicationContext的finishRefresh()方法中启动
protected void finishRefresh() {
super.finishRefresh();
//启动 tomcat
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
继续深入代码,发现是由StandardService的 addConnector()
public void addConnector(Connector connector) {
synchronized (connectorsLock) {
//忽略代码...
if (getState().isAvailable()) {
try {
//启动 connector
connector.start();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}
}
接续深入Connector的 start()方法, Connectors是LifecycleBase子类,所以最后调用的是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) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
继续跟进代码,最后进入的是NioEndpoint的成员类 Poller的 run 方法。 是的, Poller是个 Runnable, 你才的没错
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
//if we are here, means we have other stuff to do
//do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
//either we timed out or we woke up, process events first
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, attachment);
}
}//while
//process timeouts
timeout(keyCount,hasEvents);
}//while
getStopLatch().countDown();
}
上面贴了一大堆代码,也没怎么分析,不过不重要,我们只是想找到 connector的 启动到底是启动个啥。现在我们看到了:其实是启动了一个 selector , selector 是 NIO 的一个组件 (对于 NIO 感兴趣,可以看看这篇NIO简介)。selector负责监听网络事件,然后通过下面的方法处理事件:
processKey(sk, attachment);
进入 Poller 的 processKey方法,关键代码为:
if (sk.isReadable()) {
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
其中 attachment 是NioSocketWrapper类型,接下来进入processSocket()方法,一路跟踪下去,最后执行 NioEndpoint的成员类SocketProcessor的doRun()方法。
至此已经完成了对于网络时间的监听,doRun()方法是对事件的真正处理了。其中会有一些关于 HTTP 的操作,最后会转给 spring 处理,关于 spring 的部分会在下一篇文章中分析。