摘要:本文主要介紹了tomcat在啟動過程中為了處理HTTP請求之前所做的准備工作
在之前的文章中介紹了tomcat的啟動,關閉過程,后續又陸陸續續介紹了tomcat內部很多機制以及機制的源碼。大家都知道在tomcat啟動完畢以后就可以對外提供服務了,如果訪問 http://localhost:8080
就可以訪問tomcat的主頁,那么我們今天就來查看下tomcat是如何處理這個請求的,也就是如何處理http請求的。
之前我們查看了tomcat啟動的時候的部分源碼(http://www.cnblogs.com/coldridgeValley/p/5631610.html),有的地方沒有深入去看。現在我們來重點看下tomcat在啟動的過程中如何初始化接收請求的各種組件。
Connector構造函數初始化
從tomcat的架構中我們知道,Connector
組件是用來接收外部請求的,那么我們就從Connector
的初始化來說起。
我們之前的文章說過在啟動的過程中會調用Catalina
類的load()
方法,在load()
方法中有解析server.xml
的代碼如下:
//代碼段1
// Create and execute our Digester
Digester digester = createStartDigester();
在createStartDigester()
方法中關於Connector
的具體代碼如下:
//代碼段2
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
具體的規則就在ConnectorCreateRule
類中:
//代碼段3
@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"));
}
//11111
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
在標注1的地方attributes.getValue("protocol")
指的就是server.xml
中<Connector>
標簽配置的屬性:
//代碼段4
<Connector minSpareThreads="10" maxThreads="200" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
所以其實這里傳遞的是HTTP/1.1
這個字符串。我們繼續查看new Connector()
源碼。
//代碼段5
public Connector(String protocol) {
//1111
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
//222 初始化protocolHanler變量
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
查看標注1的 setProtocol()
方法源碼:
//代碼段6
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 {
//1111111111
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);
}
}
}
public void setProtocolHandlerClassName(String protocolHandlerClassName) {
this.protocolHandlerClassName = protocolHandlerClassName;
}
默認走的是標注1 的地方,所以最后就是把Connector
類的protocolHandlerClassName
成員變量賦值為org.apache.coyote.http11.Http11Protocol
。
繼續查看Connector
構造方法標注2的地方
//代碼段7
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
也就是說Connector
的初始化構造函數中把protocolHandler
這個變量初始化為Http11Protocol
的一個實例。(僅針對http請求)
Connector的init()方法初始化
我們在tomcat生命周期管理的文章中部分講解過關於Connector
的init()
方法,今天我們重新完整查看下:
//代碼段8
@Override
protected void initInternal() throws LifecycleException {
//111 調用父類 注冊到jmx
super.initInternal();
// Initialize adapter
//222 新建 CoyoteAdapter對象 將新建對象設置到protocolHandler對象中
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
try {
//333 調用protocalHander的 init()方法
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
// Initialize mapper listener
//4444 調用mapperListener的init() 方法
mapperListener.init();
}
代碼邏輯很簡單,稍微梳理下。
- 注冊到jmx
- 新建
CoyoteAdapter
對象,設置到protocalHandler
中。(重要 以后會提到) - 調用
protocalHandler
對象的init()
方法 - 調用
mapperListener
的init()
方法 (沒啥好看的,就是注冊到jmx,至於這個對象是干嘛的,后面會看到)
protocalHandler
是 Http11Protocol
的實例,查看其init()
方法之前先看下Http11Protocol
的繼承體系方便我們查看:
最先在AbstractHttp11JsseProtocol
找到了init
方法:
//代碼段9
@Override
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()
方法:
//代碼段10
@Override
public void init() throws Exception {
//略代碼
try {
//調用endpoint.init()方法
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex);
throw ex;
}
}
可以看到調用了endpoint
變量的init()
方法,而endpoint
變量的初始化是在代碼段5 初始化變量protocolHandler
的時候進行的。
//代碼段11
//調用newInstance方法等於調用該類的無參構造函數
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
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
類的init()
方法,最后在其父類AbstractEndpoint
中找到init()
方法:
//代碼段12
public final void init() throws Exception {
testServerCipherSuitesOrderSupport();
if (bindOnInit) {
//調用bind()方法,但是因為AbstractEndpoint類的`bind()`方法為抽象方法,所以這個留給子類實現,繼續去類JIoEndpoint中查找 (模版設計模式)
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
我們繼續在JIOEndpoint
類中查找bind()
方法:
//代碼段13
@Override
public void bind() throws Exception {
// Initialize thread count defaults for acceptor
//初始化 connector 默認起始鏈接數
if (acceptorThreadCount == 0) {
//如果不配置,默認是0 初始化為1
acceptorThreadCount = 1;
}
// Initialize maxConnections
// 初始化Connector的最大鏈接數
if (getMaxConnections() == 0) {
// User hasn't set a value - use the default
setMaxConnections(getMaxThreadsExecutor(true));
}
// 初始化 serverSocketFactory 用來處理 socket
if (serverSocketFactory == null) {
if (isSSLEnabled()) {
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
serverSocketFactory = new DefaultServerSocketFactory(this);
}
}
if (serverSocket == null) {
try {
if (getAddress() == null) {
//初始化一個默認跟這個JIOEndpoint管理的socket (工廠設計模式)
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
} else {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog(), getAddress());
}
} catch (BindException orig) {
//異常處理 略
}
}
}
到此就是代碼段8protocolHandler
的init()
方法調用完畢,Connector
類的init()
方法調用完畢。
Connector的start()方法初始化
tomcat啟動的時候在調用init()
方法以后,會繼續調用start()
方法,繼續查看,Connector
的start()
方法。
//代碼段14
@Override
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 {
//11111
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);
}
//22222
mapperListener.start();
}
查看Http11Protocol
類的start()
方法:
//代碼段15
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
//調用 endpoint的start()方法
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
查看JIOEndpoint
類的start()
方法:
//代碼段16
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
因為在init()
方法中,bindState
已經初始化,所以直接調用startInternal()
方法,查看JIOEndPoint()
類的startInternal()
方法:
//代碼段17
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
// 創建內部線程池 executor
if (getExecutor() == null) {
//1111
createExecutor();
}
// 初始化 connectionLimitLatch 組件(一個同步組件,給停止的時候釋放鏈接用的)
initializeConnectionLatch();
//2222
startAcceptorThreads();
// Start async timeout thread
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
查看createExecutor()
方法:
//代碼段18
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
//自定義的ThreadFactory 主要目的是為了能自定義線程池名稱
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
//新建一個線程池,最小線程數量是 getMinSpareThreads() ,最大是 getMaxThreads(),緩存時間60秒,使用 LinkedBlockingQueue 雙端無界隊列
//如果在server.xml中沒有配置的話的,默認情況 getMinSpareThreads()返回10,getMaxThreads()返回200,也就是 tomcat內部線程池默認最小線程數量10,最大200
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
public class TaskQueue extends LinkedBlockingQueue<Runnable>
繼續查看 startAcceptorThreads()
方法:
//代碼段19
protected final void startAcceptorThreads() {
//在代碼段13中 acceptorThreadCount 變量被初始化為1
int count = getAcceptorThreadCount();
//新建Acceptor 數組
acceptors = new Acceptor[count];
//for 循環賦值
for (int i = 0; i < count; i++) {
//賦值11111
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();
}
}
public int getAcceptorThreadCount() { return acceptorThreadCount; }
可以看到 代碼段19 主要做的就是新建了一個Acceptor
數組並且賦值啟動,而從代碼中可以知道 createAcceptor()
方法返回的是一個Runnable
的子類,我們查看createAcceptor()
方法:
//代碼段20 在JIOEndpoint中
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
JIOEndpoint的內部類
*/
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
//各種條件判斷略
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// socket
//1111111111
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
//異常處理 略
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused && setSocketOptions(socket)) {
// Hand this socket off to an appropriate processor
//222222222
if (!processSocket(socket)) {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} else {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} catch (IOException x) {
//異常處理 略
} catch (NullPointerException npe) {
//異常處理 略
} catch (Throwable t) {
//異常處理 略
}
}
state = AcceptorState.ENDED;
}
}
原本的源碼比較長,稍微刪減了一些並且加上了注釋。
在標注1的代碼中,serverSocketFactory
和serverSocket
變量都在代碼段13中進行了初始化,所以我們查看下acceptSocket()
方法。
//代碼段21
@Override
public Socket acceptSocket(ServerSocket socket) throws IOException {
return socket.accept();
}
很簡單就是調用了socket
的accept()
方法,但是眾所周知accept()
是阻塞的方法,所以代碼段19中新建的線程就會阻塞的這里,那這個阻塞是干什么的呢? 其實這個阻塞就是在接收外部的請求,緊接着就是標注2的地方開始處理請求了,具體的處理請求流程我們在下一篇文章中繼續。
到此就是代碼段14的Connector
的start()
方法中protocolHandler.start()
方法的源碼我們都看完了,我們繼續查看start()
方法中的mapperListener.start()
的源碼。
//代碼段22
/**
* Mapper.
*/
protected Mapper mapper = new Mapper();
/**
* Mapper listener.
*/
protected MapperListener mapperListener = new MapperListener(mapper, this);
@Override
public void startInternal() throws LifecycleException {
//設置生命周期狀態
setState(LifecycleState.STARTING);
// Find any components that have already been initialized since the
// MBean listener won't be notified as those components will have
// already regis tered their MBeans
// 設置 defaultHostName 變量
findDefaultHost();
//將engine 以及engine以下所有的自容器全部添加監聽器 mapperListener
Engine engine = (Engine) connector.getService().getContainer();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
//11111111
registerHost(host);
}
}
}
/**
* 獲取默認的 host
*/
private void findDefaultHost() {
//先獲取Engine
Engine engine = (Engine) connector.getService().getContainer();
//獲取Engine對象中配置的 defaultHost 變量 ( server.xml 中配置的)
String defaultHost = engine.getDefaultHost();
boolean found = false;
if (defaultHost != null && defaultHost.length() >0) {
//獲取engine的所有 子容器(Host)
Container[] containers = engine.findChildren();
for (Container container : containers) {
Host host = (Host) container;
//遍歷所有Host,如果Engine中默認的host名稱和 當前host名稱相同,設置found為true
if (defaultHost.equalsIgnoreCase(host.getName())) {
found = true;
break;
}
String[] aliases = host.findAliases();
//同上 只是使用host的別名查找
for (String alias : aliases) {
if (defaultHost.equalsIgnoreCase(alias)) {
found = true;
break;
}
}
}
}
if(found) {
//如果 存在就 設置 defaultHostName 變量
mapper.setDefaultHostName(defaultHost);
} else {
log.warn(sm.getString("mapperListener.unknownDefaultHost",
defaultHost, connector));
}
}
/**
* Add this mapper to the container and all child containers
*
* @param container
*/
private void addListeners(Container container) {
//把mapperListener添加到該容器的監聽器中
container.addContainerListener(this);
container.addLifecycleListener(this);
//遍歷子容器
for (Container child : container.findChildren()) {
//遞歸調用該方法,也就是把mapperListener設置到所有的容器中
addListeners(child);
}
}
可以看到mapperListener
的start()
方法中簡單的部分都直接添加了注釋,還有標注1的地方沒有說明。標注1的地方registerHost()
就不深入查看了,主要原因是內部代碼太過繁瑣,但是邏輯很清楚,所以這里就直接講解原理,不查看源碼分析了。registerHost()
主要作用是把Host
,Context
,Wrapper
容器全部注冊到mapperListener
中,以供后續方法使用!
我們查看下MapperListener
類的構造:
//代碼段23
/**
* Associated mapper.
*/
private Mapper mapper = null;
/**
* Associated connector
*/
private Connector connector = null;
可以看出mapperListener
和Connector
互相持有對方的引用。繼續看Mapper
類:
/**
* Array containing the virtual hosts definitions.
*/
Host[] hosts = new Host[0];
Mapper
內部持有Host
數組,而這個Host
並非我們常說的 那個容器類Host
:
protected static final class Host extends MapElement {
public volatile ContextList contextList;
}
這個Host
是Mapper
的一個內部類,持有了一個ContextList
對象,繼續查看:
protected static final class ContextList {
public final Context[] contexts;
}
ContexetList
依舊是Mapper
的一個內部類,持有了一個Context
數組,繼續查看Context
類的定義:
protected static final class Context extends MapElement {
public volatile ContextVersion[] versions;
public Context(String name, ContextVersion firstVersion) {
super(name, null);
versions = new ContextVersion[] { firstVersion };
}
}
Context
是Mapper
的一個內部類,持有了ContextVersion
的數組,查看ContextVersion
:
protected static final class ContextVersion extends MapElement {
public String path = null;
public int slashCount;
public String[] welcomeResources = new String[0];
public javax.naming.Context resources = null;
public Wrapper defaultWrapper = null;
public Wrapper[] exactWrappers = new Wrapper[0];
public Wrapper[] wildcardWrappers = new Wrapper[0];
public Wrapper[] extensionWrappers = new Wrapper[0];
public int nesting = 0;
public boolean mapperContextRootRedirectEnabled = false;
public boolean mapperDirectoryRedirectEnabled = false;
private volatile boolean paused;
}
可以看到,ContextVersion
中持有了 3個Wrapper
的數組。具體說到這里可以看出 MapperListener
內部包含了所有的Host
以及Host
的所有自容器,一直到Wrapper
,也許看起來不是很直觀,我們debug下看下最終MapperListener
的結構:
到此為止,整個Connector
從構造函數初始化,到啟動的時候調用init()
方法一直到初始化完畢准備接收外部請求的准備工作的源碼已經查看完畢,tomcat處理請求的源碼我們下篇文章繼續查看。