前面分析了tomcat啟動(dòng),見Tomcat啟動(dòng)源代碼分析。啟動(dòng)分析的后面已經(jīng)涉及到了對(duì)客戶端連接進(jìn)來(lái)的socket的處理的,那么今天的文章就沿著上面的文章寫下去吧。
一、Connector處理請(qǐng)求
MasterSlaveWorkerThread調(diào)用PoolTcpEndpoint進(jìn)行處理:
thereaDate是一個(gè)對(duì)象數(shù)組Object[] threadData,con是TcpConnection con = new TcpConnection()。
PoolTcpEndpoint進(jìn)行處理:
最后交由connectionHandler即Http11ConnectionHandler進(jìn)行處理:
Http11Processor進(jìn)行處理,這里是比較細(xì)節(jié)的東西:選取重要的代碼觀摩一下:
上面第一條就是處理請(qǐng)求行,一般如下:
獲取請(qǐng)求方法,請(qǐng)求的URI和協(xié)議名稱及版本。
第二條很明顯就是處理請(qǐng)求的報(bào)頭,詳見Http 協(xié)議頭基礎(chǔ)。
最后是交給Adapter進(jìn)行處理。這個(gè)Adapter是CoyoteAdapter,CoyotConnector初始化的時(shí)候設(shè)置的。見上次文章中初始化CoyotConnector部分。
CoyoteAdapter的service方法:
調(diào)用容器的invoke方法,這個(gè)容器就是StandardEngine。現(xiàn)在連接器已經(jīng)將request和response對(duì)象準(zhǔn)備好,交由容器處理了。上面的處理主要是Connector處理,流程大致如下:

下面研究容器是怎么處理請(qǐng)求的。
二、容器處理請(qǐng)求
上面說(shuō)到StandardEngine接收到請(qǐng)求,invoke方法調(diào)用。方法非常簡(jiǎn)單:
容器并不直接處理,而是交給一個(gè)pipeline的東東,這個(gè)pipeline里面會(huì)放置一些vavle,也就是說(shuō),請(qǐng)求沿著pipeline傳遞下去并且vavle對(duì)其進(jìn)行相關(guān)的處理。這個(gè)valve有簡(jiǎn)單的,也有復(fù)雜的,簡(jiǎn)單的就是起個(gè)接力棒的作用,復(fù)雜的可以對(duì)請(qǐng)求做一些處理,比如說(shuō)日志等,valve還可以自定義,查看server.xml配置文件就知道了。相關(guān)類圖如下:

具體可以看一下幾個(gè)Valve的功能:
這個(gè)valve驗(yàn)證對(duì)象實(shí)例的正確性,選擇Host并交由其處理。
Host的vavle也是先判斷對(duì)象實(shí)例,然后選擇相應(yīng)的Context,查看是否有session信息并更新其最后獲取時(shí)間(tomcat的session研究放在后面,東西太多了),最后才調(diào)用context的invoke方法。StandardContext首先check context是否在reload,如果reload結(jié)束或者沒有reload那么就和上面幾個(gè)容器一樣,通過(guò)valve處理:
常規(guī)檢查以后是安全請(qǐng)求檢查,然后得到一個(gè)wrapper,這個(gè)wrapper是啥呢?這就得StandardContextMapper,它的map方法大家可以研究一下,其實(shí)就是通過(guò)請(qǐng)求路徑查找相應(yīng)class的過(guò)程,查找的優(yōu)先級(jí)一目了然,首先是精確匹配,然后是前綴匹配,擴(kuò)展匹配最后是默認(rèn)匹配。Wrapper其實(shí)就是封裝了一個(gè)servlet。其valve類StandardWrapperValve的invoke方法有點(diǎn)長(zhǎng),大致功能如下:實(shí)例檢查,可獲得性檢查,獲取servlet實(shí)例,F(xiàn)ilter和Servlet執(zhí)行。這個(gè)時(shí)候就會(huì)調(diào)用servlet的service方法,交給我們自定義的servlet進(jìn)行實(shí)際的業(yè)務(wù)處理。
一、Connector處理請(qǐng)求
MasterSlaveWorkerThread調(diào)用PoolTcpEndpoint進(jìn)行處理:
// Process the request from this socket
endpoint.processSocket(socket, con, threadData);
endpoint.processSocket(socket, con, threadData);
thereaDate是一個(gè)對(duì)象數(shù)組Object[] threadData,con是TcpConnection con = new TcpConnection()。
PoolTcpEndpoint進(jìn)行處理:
void processSocket(Socket s, TcpConnection con, Object[] threadData) {
// Process the connection
int step = 1;
try {
// 1: Set socket options: timeout, linger, etc
setSocketOptions(s);
// 2: SSL handshake
step = 2;
if (getServerSocketFactory() != null) {
getServerSocketFactory().handshake(s);
}
// 3: Process the connection
step = 3;
con.setEndpoint(this);
con.setSocket(s);
getConnectionHandler().processConnection(con, threadData);
} catch (SocketException se) {
log.debug(sm.getString("endpoint.err.socket", s.getInetAddress()),
se);
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} catch (Throwable t) {
if (step == 2) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.handshake"), t);
}
} else {
log.error(sm.getString("endpoint.err.unexpected"), t);
}
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} finally {
if (con != null) {
con.recycle();
}
}
}
// Process the connection
int step = 1;
try {
// 1: Set socket options: timeout, linger, etc
setSocketOptions(s);
// 2: SSL handshake
step = 2;
if (getServerSocketFactory() != null) {
getServerSocketFactory().handshake(s);
}
// 3: Process the connection
step = 3;
con.setEndpoint(this);
con.setSocket(s);
getConnectionHandler().processConnection(con, threadData);
} catch (SocketException se) {
log.debug(sm.getString("endpoint.err.socket", s.getInetAddress()),
se);
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} catch (Throwable t) {
if (step == 2) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.handshake"), t);
}
} else {
log.error(sm.getString("endpoint.err.unexpected"), t);
}
// Try to close the socket
try {
s.close();
} catch (IOException e) {
}
} finally {
if (con != null) {
con.recycle();
}
}
}
最后交由connectionHandler即Http11ConnectionHandler進(jìn)行處理:
public void processConnection(TcpConnection connection,
Object thData[]) {
Socket socket=null;
Http11Processor processor=null;
try {
processor=(Http11Processor)thData[Http11BaseProtocol.THREAD_DATA_PROCESSOR];
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_START, null);
}
socket=connection.getSocket();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
if( proto.secure ) {
SSLSupport sslSupport=null;
if(proto.sslImplementation != null)
sslSupport = proto.sslImplementation.getSSLSupport(socket);
processor.setSSLSupport(sslSupport);
} else {
processor.setSSLSupport( null );
}
processor.setSocket( socket );
processor.process(in, out);
// If unread input arrives after the shutdownInput() call
// below and before or during the socket.close(), an error
// may be reported to the client. To help troubleshoot this
// type of error, provide a configurable delay to give the
// unread input time to arrive so it can be successfully read
// and discarded by shutdownInput().
if( proto.socketCloseDelay >= 0 ) {
try {
Thread.sleep(proto.socketCloseDelay);
} catch (InterruptedException ie) { /* ignore */ }
}
TcpConnection.shutdownInput( socket );
} catch(java.net.SocketException e) {
// SocketExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.socketexception.debug"), e);
} catch (IOException e) {
// IOExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.ioexception.debug"), e);
}
// Future developers: if you discover any other
// rare-but-nonfatal exceptions, catch them here, and log as
// above.
catch (Throwable e) {
// any other exception or error is odd. Here we log it
// with "ERROR" level, so it will show up even on
// less-than-verbose logs.
Http11BaseProtocol.log.error
(sm.getString("http11protocol.proto.error"), e);
} finally {
// if(proto.adapter != null) proto.adapter.recycle();
// processor.recycle();
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
}
// recycle kernel sockets ASAP
try { if (socket != null) socket.close (); }
catch (IOException e) { /* ignore */ }
}
}
}
Object thData[]) {
Socket socket=null;
Http11Processor processor=null;
try {
processor=(Http11Processor)thData[Http11BaseProtocol.THREAD_DATA_PROCESSOR];
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_START, null);
}
socket=connection.getSocket();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
if( proto.secure ) {
SSLSupport sslSupport=null;
if(proto.sslImplementation != null)
sslSupport = proto.sslImplementation.getSSLSupport(socket);
processor.setSSLSupport(sslSupport);
} else {
processor.setSSLSupport( null );
}
processor.setSocket( socket );
processor.process(in, out);
// If unread input arrives after the shutdownInput() call
// below and before or during the socket.close(), an error
// may be reported to the client. To help troubleshoot this
// type of error, provide a configurable delay to give the
// unread input time to arrive so it can be successfully read
// and discarded by shutdownInput().
if( proto.socketCloseDelay >= 0 ) {
try {
Thread.sleep(proto.socketCloseDelay);
} catch (InterruptedException ie) { /* ignore */ }
}
TcpConnection.shutdownInput( socket );
} catch(java.net.SocketException e) {
// SocketExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.socketexception.debug"), e);
} catch (IOException e) {
// IOExceptions are normal
Http11BaseProtocol.log.debug
(sm.getString
("http11protocol.proto.ioexception.debug"), e);
}
// Future developers: if you discover any other
// rare-but-nonfatal exceptions, catch them here, and log as
// above.
catch (Throwable e) {
// any other exception or error is odd. Here we log it
// with "ERROR" level, so it will show up even on
// less-than-verbose logs.
Http11BaseProtocol.log.error
(sm.getString("http11protocol.proto.error"), e);
} finally {
// if(proto.adapter != null) proto.adapter.recycle();
// processor.recycle();
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
}
// recycle kernel sockets ASAP
try { if (socket != null) socket.close (); }
catch (IOException e) { /* ignore */ }
}
}
}
Http11Processor進(jìn)行處理,這里是比較細(xì)節(jié)的東西:選取重要的代碼觀摩一下:
inputBuffer.parseRequestLine();
inputBuffer.parseHeaders();
adapter.service(request, response);
inputBuffer.parseHeaders();
adapter.service(request, response);
上面第一條就是處理請(qǐng)求行,一般如下:
POST /loan/control/customer HTTP/1.0
獲取請(qǐng)求方法,請(qǐng)求的URI和協(xié)議名稱及版本。
第二條很明顯就是處理請(qǐng)求的報(bào)頭,詳見Http 協(xié)議頭基礎(chǔ)。
最后是交給Adapter進(jìn)行處理。這個(gè)Adapter是CoyoteAdapter,CoyotConnector初始化的時(shí)候設(shè)置的。見上次文章中初始化CoyotConnector部分。
CoyoteAdapter的service方法:
/**
* Service method.
*/
public void service(Request req, Response res)
throws Exception {
CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = (CoyoteRequest) connector.createRequest();
request.setCoyoteRequest(req);
response = (CoyoteResponse) connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
try {
// Parse and set Catalina and configuration specific
// request parameters
postParseRequest(req, request, res, response);
// Calling the container
connector.getContainer().invoke(request, response);
response.finishResponse();
req.action( ActionCode.ACTION_POST_REQUEST , null);
} catch (IOException e) {
;
} catch (Throwable t) {
log(sm.getString("coyoteAdapter.service"), t);
} finally {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}
}
* Service method.
*/
public void service(Request req, Response res)
throws Exception {
CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = (CoyoteRequest) connector.createRequest();
request.setCoyoteRequest(req);
response = (CoyoteResponse) connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
try {
// Parse and set Catalina and configuration specific
// request parameters
postParseRequest(req, request, res, response);
// Calling the container
connector.getContainer().invoke(request, response);
response.finishResponse();
req.action( ActionCode.ACTION_POST_REQUEST , null);
} catch (IOException e) {
;
} catch (Throwable t) {
log(sm.getString("coyoteAdapter.service"), t);
} finally {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}
}
調(diào)用容器的invoke方法,這個(gè)容器就是StandardEngine。現(xiàn)在連接器已經(jīng)將request和response對(duì)象準(zhǔn)備好,交由容器處理了。上面的處理主要是Connector處理,流程大致如下:
下面研究容器是怎么處理請(qǐng)求的。
二、容器處理請(qǐng)求
上面說(shuō)到StandardEngine接收到請(qǐng)求,invoke方法調(diào)用。方法非常簡(jiǎn)單:
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}
throws IOException, ServletException {
pipeline.invoke(request, response);
}
容器并不直接處理,而是交給一個(gè)pipeline的東東,這個(gè)pipeline里面會(huì)放置一些vavle,也就是說(shuō),請(qǐng)求沿著pipeline傳遞下去并且vavle對(duì)其進(jìn)行相關(guān)的處理。這個(gè)valve有簡(jiǎn)單的,也有復(fù)雜的,簡(jiǎn)單的就是起個(gè)接力棒的作用,復(fù)雜的可以對(duì)請(qǐng)求做一些處理,比如說(shuō)日志等,valve還可以自定義,查看server.xml配置文件就知道了。相關(guān)類圖如下:
具體可以看一下幾個(gè)Valve的功能:
StandardEngineValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Validate that any HTTP/1.1 request included a host header
HttpServletRequest hrequest = (HttpServletRequest) request;
if ("HTTP/1.1".equals(hrequest.getProtocol()) &&
(hrequest.getServerName() == null)) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHostHeader",
request.getRequest().getServerName()));
return;
}
// Select the Host to be used for this Request
StandardEngine engine = (StandardEngine) getContainer();
Host host = (Host) engine.map(request, true);
if (host == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getRequest().getServerName()));
return;
}
// Ask this Host to process this request
host.invoke(request, response);
}
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Validate that any HTTP/1.1 request included a host header
HttpServletRequest hrequest = (HttpServletRequest) request;
if ("HTTP/1.1".equals(hrequest.getProtocol()) &&
(hrequest.getServerName() == null)) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHostHeader",
request.getRequest().getServerName()));
return;
}
// Select the Host to be used for this Request
StandardEngine engine = (StandardEngine) getContainer();
Host host = (Host) engine.map(request, true);
if (host == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getRequest().getServerName()));
return;
}
// Ask this Host to process this request
host.invoke(request, response);
}
這個(gè)valve驗(yàn)證對(duì)象實(shí)例的正確性,選擇Host并交由其處理。
StandardHostValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Select the Context to be used for this Request
StandardHost host = (StandardHost) getContainer();
Context context = (Context) host.map(request, true);
if (context == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
// Bind the context CL to the current thread
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
// Update the session last access time for our session (if any)
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String sessionId = hreq.getRequestedSessionId();
if (sessionId != null) {
Manager manager = context.getManager();
if (manager != null) {
Session session = manager.findSession(sessionId);
if ((session != null) && session.isValid())
session.access();
}
}
// Ask this Context to process this request
context.invoke(request, response);
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Select the Context to be used for this Request
StandardHost host = (StandardHost) getContainer();
Context context = (Context) host.map(request, true);
if (context == null) {
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
// Bind the context CL to the current thread
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
// Update the session last access time for our session (if any)
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String sessionId = hreq.getRequestedSessionId();
if (sessionId != null) {
Manager manager = context.getManager();
if (manager != null) {
Session session = manager.findSession(sessionId);
if ((session != null) && session.isValid())
session.access();
}
}
// Ask this Context to process this request
context.invoke(request, response);
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
Host的vavle也是先判斷對(duì)象實(shí)例,然后選擇相應(yīng)的Context,查看是否有session信息并更新其最后獲取時(shí)間(tomcat的session研究放在后面,東西太多了),最后才調(diào)用context的invoke方法。StandardContext首先check context是否在reload,如果reload結(jié)束或者沒有reload那么就和上面幾個(gè)容器一樣,通過(guò)valve處理:
StandardContextValve:
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Disallow any direct access to resources under WEB-INF or META-INF
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI =
requestURI.substring(contextPath.length()).toUpperCase();
if (relativeURI.equals("/META-INF") ||
relativeURI.equals("/WEB-INF") ||
relativeURI.startsWith("/META-INF/") ||
relativeURI.startsWith("/WEB-INF/")) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
Context context = (Context) getContainer();
// Select the Wrapper to be used for this Request
Wrapper wrapper = null;
try {
wrapper = (Wrapper) context.map(request, true);
} catch (IllegalArgumentException e) {
badRequest(requestURI,
(HttpServletResponse) response.getResponse());
return;
}
if (wrapper == null) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
// Ask this Wrapper to process this Request
response.setContext(context);
wrapper.invoke(request, response);
}
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return; // NOTE - Not much else we can do generically
}
// Disallow any direct access to resources under WEB-INF or META-INF
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI =
requestURI.substring(contextPath.length()).toUpperCase();
if (relativeURI.equals("/META-INF") ||
relativeURI.equals("/WEB-INF") ||
relativeURI.startsWith("/META-INF/") ||
relativeURI.startsWith("/WEB-INF/")) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
Context context = (Context) getContainer();
// Select the Wrapper to be used for this Request
Wrapper wrapper = null;
try {
wrapper = (Wrapper) context.map(request, true);
} catch (IllegalArgumentException e) {
badRequest(requestURI,
(HttpServletResponse) response.getResponse());
return;
}
if (wrapper == null) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return;
}
// Ask this Wrapper to process this Request
response.setContext(context);
wrapper.invoke(request, response);
}
常規(guī)檢查以后是安全請(qǐng)求檢查,然后得到一個(gè)wrapper,這個(gè)wrapper是啥呢?這就得StandardContextMapper,它的map方法大家可以研究一下,其實(shí)就是通過(guò)請(qǐng)求路徑查找相應(yīng)class的過(guò)程,查找的優(yōu)先級(jí)一目了然,首先是精確匹配,然后是前綴匹配,擴(kuò)展匹配最后是默認(rèn)匹配。Wrapper其實(shí)就是封裝了一個(gè)servlet。其valve類StandardWrapperValve的invoke方法有點(diǎn)長(zhǎng),大致功能如下:實(shí)例檢查,可獲得性檢查,獲取servlet實(shí)例,F(xiàn)ilter和Servlet執(zhí)行。這個(gè)時(shí)候就會(huì)調(diào)用servlet的service方法,交給我們自定義的servlet進(jìn)行實(shí)際的業(yè)務(wù)處理。