文章目錄
我們都知道,一個http請求過程一般爲瀏覽器發送請求->建立socket連接->通過socket讀取數據->解析http數據->調用後臺服務完成響應,而對於Tomcat來說,着重點在建立socket連接->通過socket讀取數據->解析http數據->調用filter->調用servlet處理並返回響應,本文將詳細介紹Tomcat請求過程。
前言
當Tomcat啓動時,會啓動一組線程用於不同階段的請求處理過程。
從圖中可以看出,根據不同協議分別啓動了Acceptor和Poller線程,先介紹一下這兩個線程的作用,這裏只對http協議的請求過程進行分析。
- Acceptor:用於接收新連接,並將新連接添加到
Poller
事件隊列中。 - Poller:用於監聽Socket事件,當Socket可讀或者可寫時,調用線程池處理Socket請求。
建立Socket連接
Acceptor#run
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (endpoint.isRunning()) {
try {
//...
U socket = null;
// 監聽socket負責接收新連接
socket = endpoint.serverSocketAccept();
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (endpoint.isRunning() && !endpoint.isPaused()) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
//處理接受到的socket對象併發布事件
if (!endpoint.setSocketOptions(socket)) {
endpoint.closeSocket(socket);
}
} else {
endpoint.destroySocket(socket);
}
} catch (Throwable t) {
//...
}
}
state = AcceptorState.ENDED;
}
Acceptor
在啓動後會阻塞在endpoint.serverSocketAccept();
方法處,當有新連接到達時,該方法返回一個SocketChannel
。這裏的endpoint
爲NioEndpoint
。
protected SocketChannel serverSocketAccept() throws Exception {
return serverSock.accept();
}
endpoint.setSocketOptions(socket)
會將socket封裝到NioChannel
中,並註冊到Poller。
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
// Disable blocking, polling will be used
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
NioChannel channel = null;
if (nioChannels != null) {
channel = nioChannels.pop();
}
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
channel = new NioChannel(socket, bufhandler);
}
} else {
channel.setIOChannel(socket);
channel.reset();
}
//獲取Poller並將socket註冊到Poller當中
getPoller0().register(channel);
//...
return true;
}
發佈PollerEvent事件
NioEndpoint
維護了一個 Poller
數組,使用時,從中取出一個。如果當前Poller線程只有一個時,默認返回pollers[0],否則當一個連接分配給 pollers[index]
時,下一個連接就會分配給 pollers[(index+1)%pollers.length]
。
public Poller getPoller0() {
if (pollerThreadCount == 1) {
return pollers[0];
} else {
int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
return pollers[idx];
}
}
然後將 Socket 添加到該Poller
的PollerEvent
隊列中。
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper socketWrapper = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(socketWrapper);
socketWrapper.setPoller(this);
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
socketWrapper.setSecure(isSSLEnabled());
socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
PollerEvent r = null;
if (eventCache != null) {
r = eventCache.pop();
}
if (r == null) {
r = new PollerEvent(socket, socketWrapper, OP_REGISTER);
} else {
r.reset(socket, socketWrapper, OP_REGISTER);
}
// 添加到PollerEvent隊列當中
addEvent(r);
}
private void addEvent(PollerEvent event) {
// 添加到PollerEvent隊列當中
events.offer(event);
if (wakeupCounter.incrementAndGet() == 0) {
selector.wakeup();
}
}
處理PollerEvent事件
Poller
線程會消費PollerEvent
隊列
Poller#run
public void run() {
//..
if (!close) {
//這裏會處理PollerEvent事件
hasEvents = events();
}
//...
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
if (socketWrapper == null) {
iterator.remove();
} else {
iterator.remove();
//處理socket請求
processKey(sk, socketWrapper);
}
}
}
events()
處理PollerEvent事件並將socket註冊到selector當中,省略了部分代碼,Poller和PollerEvent都是NioEndpoint
的內部類。
public class Poller implements Runnable {
public boolean events() {
boolean result = false;
PollerEvent pe = null;
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
//調用PollerEvent的run
pe.run();
pe.reset();
if (running && !paused && eventCache != null) {
eventCache.push(pe);
}
}
return result;
}
}
public static class PollerEvent implements Runnable {
public void run() {
if (interestOps == OP_REGISTER) {
//將Socket註冊到channel中
socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
}
}
}
processKey(sk, socketWrapper)
處理socket數據。
Poller#processKey
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
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;
}
}
}
當Socket可讀或者可寫時,處理Socket數據。
讀取Socket數據
AbstractEndpoint#processSocket
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = null;
if (processorCache != null) {
sc = processorCache.pop();
}
// 1.創建socket數據處理器
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
//2.啓動線程處理socket數據,
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
return true;
}
- 創建socket數據處理器
**NioEndpoint#createSocketProcessor **
protected SocketProcessorBase<NioChannel> createSocketProcessor(
SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
return new SocketProcessor(socketWrapper, event);
}
- 啓動線程處理socket數據。如果配置了線程池會啓動線程池來執行。
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
int handshake = -1;
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
// 處理socket數據
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
close(socket, key);
}
} else if (handshake == -1 ) {
close(socket, key);
} else if (handshake == SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
} else if (handshake == SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
}
}
這裏的handler爲ConnectionHandler
,來源於Http11NioProtocol
構造方法。
public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
public Http11NioProtocol() {
super(new NioEndpoint());
}
}
public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
super(endpoint);
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
}
}
那麼接下來聚焦於它的process
方法。
ConnectionHandler#process
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
//...
S socket = wrapper.getSocket();
Processor processor = connections.get(socket);
//....
if (processor == null) {
//創建processor
processor = getProtocol().createProcessor();
register(processor);
}
SocketState state = SocketState.CLOSED;
do {
//處理socket
state = processor.process(wrapper, status);
//...省略了處理完成之後清理socket的操作
} while ( state == SocketState.UPGRADING);
// Make sure socket/processor is removed from the list of current
// connections
connections.remove(socket);
release(processor);
return SocketState.CLOSED;
}
默認一個新連接的情況下,會調用processor.process(wrapper, status)
代碼,而processor的實例一般是在getProtocol().createProcessor()
創建的,而protocol則:
public ConnectionHandler(AbstractProtocol<S> proto) {
this.proto = proto;
}
protected AbstractProtocol<S> getProtocol() {
return proto;
}
而AbstractProtocol
有兩個實現類,AbstractHttp11Protocol
和AbstractAjpProtocol
,這裏是Http請求,所以獲取到的protocol爲AbstractHttp11Protocol
。而AbstractHttp11Protocol
創建了Http11Processor
實例。
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(this, adapter);
return processor;
}
由於Http11Processor
沒有實現process方法,所以實際調用了其父類的父類AbstractProcessorLight
的process方法。
public abstract class AbstractProcessorLight implements Processor {
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
//...
if (status == SocketEvent.OPEN_READ){
//
state = service(socketWrapper);
} else {
state = SocketState.CLOSED;
}
} while (state == SocketState.ASYNC_END ||
dispatches != null && state != SocketState.CLOSED);
return state;
}
}
根據不同的SocketEvent做不同操作,這裏的事件是OPEN_READ,所以調用service
方法解析http數據。
解析Http數據
Http11Processor#service
public SocketState service(SocketWrapperBase<?> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
inputBuffer.init(socketWrapper);
outputBuffer.init(socketWrapper);
// Flags
while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
sendfileState == SendfileState.DONE && !protocol.isPaused()) {
// Parsing the request header
// 解析requestLine
if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
protocol.getKeepAliveTimeout())) {
if (inputBuffer.getParsingRequestLinePhase() == -1) {
return SocketState.UPGRADING;
} else if (handleIncompleteRequestLineRead()) {
break;
}
}
if (protocol.isPaused()) {
response.setStatus(503);
setErrorState(ErrorState.CLOSE_CLEAN, null);
} else {
keptAlive = true;
request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
//解析header
if (!inputBuffer.parseHeaders()) {
openSocket = true;
readComplete = false;
break;
}
if (!protocol.getDisableUploadTimeout()) {
socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
}
}
//...
//指定request body的讀取filter
prepareRequest();
//...
// Process the request in the adapter
if (getErrorState().isIoAllowed()) {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
//處理request
getAdapter().service(request, response);
if(keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
}
// Finish the handling of the request
//...
}
}
- 解析請求header。
- 指定request body的讀取filter。
- 匹配servlet。
這裏主要針對匹配servlet作詳細解釋。
CoyoteAdapter#service
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
//...
// 解析request並匹配servlet
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// 調用匹配成功的servlet
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
//...
}
- 解析request並匹配servlet
- 調用匹配成功的servlet
匹配servlet
protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
org.apache.coyote.Response res, Response response) throws IOException, ServletException {
//...
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
//..
}
這裏只關注如何匹配,省略了一些有關seesion和cookie的處理。
Mapper在StandardService
啓動時MapperListener
的start方法初始化。MapperListener啓動這裏放一張截圖方便理解。
Mapper#map
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData) throws IOException {
if (host.isNull()) {
String defaultHostName = this.defaultHostName;
if (defaultHostName == null) {
return;
}
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
//匹配context(即war包)名稱,設置到mappingData中,匹配成功後匹配context的child wrapper名稱,設置到mappingData中。
internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
if (mappingData.host != null) {
throw new AssertionError();
}
//...
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, uri);
if (pos == -1) {
return;
}
int lastSlash = -1;
int uriEnd = uri.getEnd();
int length = -1;
boolean found = false;
MappedContext context = null;
while (pos >= 0) {
context = contexts[pos];
//匹配Context
if (uri.startsWith(context.name)) {
length = context.name.length();
if (uri.getLength() == length) {
found = true;
break;
} else if (uri.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = nthSlash(uri, contextList.nesting + 1);
} else {
lastSlash = lastSlash(uri);
}
uri.setEnd(lastSlash);
pos = find(contexts, uri);
}
uri.setEnd(uriEnd);
if (!found) {
if (contexts[0].name.equals("")) {
context = contexts[0];
} else {
context = null;
}
}
if (context == null) {
return;
}
mappingData.contextPath.setString(context.name);
//匹配contextversion
ContextVersion contextVersion = null;
ContextVersion[] contextVersions = context.versions;
final int versionCount = contextVersions.length;
if (versionCount > 1) {
Context[] contextObjects = new Context[contextVersions.length];
for (int i = 0; i < contextObjects.length; i++) {
contextObjects[i] = contextVersions[i].object;
}
mappingData.contexts = contextObjects;
if (version != null) {
contextVersion = exactFind(contextVersions, version);
}
}
if (contextVersion == null) {
// Return the latest version
// The versions array is known to contain at least one element
contextVersion = contextVersions[versionCount - 1];
}
mappingData.context = contextVersion.object;
mappingData.contextSlashCount = contextVersion.slashCount;
// Wrapper mapping 匹配wrapper
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
這裏主要分爲兩步進行匹配。
- 首先匹配
context.name
,匹配成功後設置到mappingData.contextPath
。 - 匹配
wrapper.name
,匹配成功後設置到mappingData.wrapperPath
。
private final void internalMapWrapper(ContextVersion contextVersion,
CharChunk path,
MappingData mappingData) throws IOException {
//...
// Rule 1 -- Exact Match 匹配真實的wrapper,即項目裏自己寫的
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 2 -- Prefix Match 通配匹配
boolean checkJspWelcomeFiles = false;
MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
if (mappingData.wrapper == null) {
internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
if (mappingData.wrapper != null && mappingData.jspWildCard) {
char[] buf = path.getBuffer();
if (buf[pathEnd - 1] == '/') {
mappingData.wrapper = null;
checkJspWelcomeFiles = true;
} else {
mappingData.wrapperPath.setChars(buf, path.getStart(),
path.getLength());
mappingData.pathInfo.recycle();
}
}
}
//...
// Rule 3 -- Extension Match 擴展匹配
MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
}
// Rule 4 -- Welcome resources processing for servlets
if (mappingData.wrapper == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
char[] buf = path.getBuffer();
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
path.append(contextVersion.welcomeResources[i], 0,
contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
// Rule 4a -- Welcome resources processing for exact macth
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.wrapper == null) {
internalMapWildcardWrapper
(wildcardWrappers, contextVersion.nesting,
path, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.wrapper == null
&& contextVersion.resources != null) {
String pathStr = path.toString();
WebResource file =
contextVersion.resources.getResource(pathStr);
if (file != null && file.isFile()) {
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, true);
if (mappingData.wrapper == null
&& contextVersion.defaultWrapper != null) {
mappingData.wrapper =
contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
}
}
}
path.setOffset(servletPath);
path.setEnd(pathEnd);
}
}
//...
// Rule 7 -- Default servlet
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
if (contextVersion.defaultWrapper != null) {
mappingData.wrapper = contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.matchType = MappingMatch.DEFAULT;
}
// Redirection to a folder
char[] buf = path.getBuffer();
if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
String pathStr = path.toString();
// Note: Check redirect first to save unnecessary getResource()
// call. See BZ 62968.
if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
WebResource file;
// Handle context root
if (pathStr.length() == 0) {
file = contextVersion.resources.getResource("/");
} else {
file = contextVersion.resources.getResource(pathStr);
}
if (file != null && file.isDirectory()) {
// Note: this mutates the path: do not do any processing
// after this (since we set the redirectPath, there
// shouldn't be any)
path.setOffset(pathOffset);
path.append('/');
mappingData.redirectPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
} else {
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
} else {
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
}
}
path.setOffset(pathOffset);
path.setEnd(pathEnd);
}
匹配規則:
- 匹配
exact servlet
,即項目裏真實的servlet。 - 通用匹配,
- 擴展匹配,tomcat容器
web.xml
默認的servlet - reources匹配,即歡迎頁面等
- 匹配未配置的擴展名映射,如index.jsf,index.do。
default servlet
的匹配。
這裏重點看一下匹配exact servlet
。
private final void internalMapExactWrapper
(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
//根據path尋找wrapper
MappedWrapper wrapper = exactFind(wrappers, path);
if (wrapper != null) {
mappingData.requestPath.setString(wrapper.name);
mappingData.wrapper = wrapper.object;
if (path.equals("/")) {//根目錄
// Special handling for Context Root mapped servlet
mappingData.pathInfo.setString("/");
mappingData.wrapperPath.setString("");
// This seems wrong but it is what the spec says...
mappingData.contextPath.setString("");
mappingData.matchType = MappingMatch.CONTEXT_ROOT;
} else {
mappingData.wrapperPath.setString(wrapper.name);
mappingData.matchType = MappingMatch.EXACT;
}
}
}
匹配成功後,將wrapper.name
和matchType
設置到mappingData
中以備調用servlet時使用。這裏放一張截圖方便理解。
到這裏postParseRequest
就完成了。
調用servlet
CoyoteAdapter#service
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
這裏通過調用每個容器的pipeline
的Valve對象,實現了對Servlet的調用,每個容器都有一個或者多個Valve
,這些Valve
來源於兩個地方,一個是conf/server.xml
中配置的Valve
,另外一個就是每一個容器的構造其中自己初始化的Valve
。
這裏調用了StandardEngineValve.invoke
,然後繼續往下調用。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
通過debug代碼,可以得到一個完整的調用鏈。
->org.apache.catalina.core.StandardEngineValve#invoke
-->org.apache.catalina.valves.AbstractAccessLogValve#invoke
--->org.apache.catalina.valves.ErrorReportValve#invoke
---->org.apache.catalina.core.StandardHostValve#invoke
----->org.apache.catalina.authenticator.AuthenticatorBase#invoke
------>org.apache.catalina.core.StandardContextValve#invoke
------->org.apache.catalina.core.StandardWrapperValve#invoke
因爲StandardWrapperValve
中最終調用了servlet,所以重點看下。
final class StandardWrapperValve extends ValveBase {
public final void invoke(Request request, Response response) throws IOException, ServletException {
//...
if (!unavailable) {
//實例化servlet
servlet = wrapper.allocate();
}
//...
//create filter調用鏈
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
//...
//調用filter
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
}
- 實例化servlet。
- 創建filter調用鏈。
- 調用filter
創建filter調用鏈
ApplicationFilterFactory#createFilterChain
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null)
return null;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 獲取啓動時初始化的filter
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
// 如果沒有filter 直接返回
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
//校驗requestPath是否匹配filter的urlPatterns
// Add the relevant path-mapped filters to this filter chain
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
//校驗servlet name是否匹配filter的servletNames
// Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
- 首先創建並初始化一個
filterChain
對象。 - 循環調用
matchFiltersURL(filterMaps[i], requestPath)
匹配requestPath
與filter的urlPatterns
,requestPath
即servlet路徑,匹配成功後將當前filter添加到filterChain
。
private static boolean matchFiltersURL(FilterMap filterMap, String requestPath) {
// Check the specific "*" special URL pattern, which also matches
// named dispatches
if (filterMap.getMatchAllUrlPatterns())
return true;
if (requestPath == null)
return false;
// Match on context relative request path
String[] testPaths = filterMap.getURLPatterns();
for (int i = 0; i < testPaths.length; i++) {
if (matchFiltersURL(testPaths[i], requestPath)) {
return true;
}
}
// No match
return false;
}
private static boolean matchFiltersURL(String testPath, String requestPath) {
if (testPath == null)
return false;
// Case 1 - Exact Match
if (testPath.equals(requestPath))
return true;
// Case 2 - Path Match ("/.../*")
if (testPath.equals("/*"))
return true;
if (testPath.endsWith("/*")) {
if (testPath.regionMatches(0, requestPath, 0,
testPath.length() - 2)) {
if (requestPath.length() == (testPath.length() - 2)) {
return true;
} else if ('/' == requestPath.charAt(testPath.length() - 2)) {
return true;
}
}
return false;
}
// Case 3 - Extension Match
if (testPath.startsWith("*.")) {
int slash = requestPath.lastIndexOf('/');
int period = requestPath.lastIndexOf('.');
if ((slash >= 0) && (period > slash)
&& (period != requestPath.length() - 1)
&& ((requestPath.length() - period)
== (testPath.length() - 1))) {
return testPath.regionMatches(2, requestPath, period + 1,
testPath.length() - 2);
}
}
// Case 4 - "Default" Match
return false; // NOTE - Not relevant for selecting filters
}
- 循環調用
matchFiltersServlet(filterMaps[i], servletName)
匹配servletName
與filter的servletNames
,匹配成功後將當前filter添加到filterChain
。
private static boolean matchFiltersServlet(FilterMap filterMap,
String servletName) {
if (servletName == null) {
return false;
}
// Check the specific "*" special servlet name
else if (filterMap.getMatchAllServletNames()) {
return true;
} else {
String[] servletNames = filterMap.getServletNames();
for (int i = 0; i < servletNames.length; i++) {
if (servletName.equals(servletNames[i])) {
return true;
}
}
return false;
}
}
到這裏創建filter調用鏈就結束了,開始調用filter。
調用Filter
ApplicationFilterChain#doFilter
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
//...
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// n=filter.length ,addFilter時初始化。
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
//調用filter
filter.doFilter(request, response, this);
return;
}
//...
//filter調用完成之後調用servlet的service方法。
servlet.service(request, response);
}
調用servlet
最後會調用HttpServlet的service方法來區分調用GET/POST等方法。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
以上。
總結
通過上面的分析,其實我們已經清楚了當一個請求過來以後,Tomcat是如何處理的。我們再來做一個總體的總結:
- 用戶瀏覽器發送請求,請求會發送到對應的Connector監聽的Socket端口。
- Connector從Socket流中獲取數據,然後根據Http協議將其解析爲Request和Reponse對象。
- 找到Request對象對應的Host,Context,Wrapper。
- 調用filter鏈。
- 調用最終的Servelt的service進行處理。
最後放一張整體的調用鏈。
參考:
https://blog.csdn.net/jiaomingliang/article/details/47414657
https://www.jianshu.com/p/d8a2bc7d3c21