本文主要是介绍Tomcat源码解析:请求处理过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 前言
- 建立Socket连接
- 发布PollerEvent事件
- 处理PollerEvent事件
- 读取Socket数据
- 解析Http数据
- 匹配servlet
- 调用servlet
- 创建filter调用链
- 调用Filter
- 调用servlet
- 总结
我们都知道,一个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 commandwhile (endpoint.isRunning()) {try {//...U socket = null;// 监听socket负责接收新连接socket = endpoint.serverSocketAccept();// Successful accept, reset the error delayerrorDelay = 0;// Configure the socketif (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 usedsocket.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的runpe.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) {//创建processorprocessor = getProtocol().createProcessor();register(processor);}SocketState state = SocketState.CLOSED;do {//处理socketstate = processor.process(wrapper, status);//...省略了处理完成之后清理socket的操作} while ( state == SocketState.UPGRADING);// Make sure socket/processor is removed from the list of current// connectionsconnections.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 {@Overridepublic 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/OsetSocketWrapper(socketWrapper);inputBuffer.init(socketWrapper);outputBuffer.init(socketWrapper);// Flagswhile (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&sendfileState == SendfileState.DONE && !protocol.isPaused()) {// Parsing the request header// 解析requestLineif (!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());//解析headerif (!inputBuffer.parseHeaders()) {openSocket = true;readComplete = false;break;}if (!protocol.getDisableUploadTimeout()) {socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());}}//...//指定request body的读取filterprepareRequest();//...// Process the request in the adapterif (getErrorState().isIoAllowed()) {rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);//处理requestgetAdapter().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并匹配servletpostParseSuccess = postParseRequest(req, request, res, response);if (postParseSuccess) {request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());// 调用匹配成功的servletconnector.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];//匹配Contextif (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);//匹配contextversionContextVersion 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 elementcontextVersion = contextVersions[versionCount - 1];}mappingData.context = contextVersion.object;mappingData.contextSlashCount = contextVersion.slashCount;// Wrapper mapping 匹配wrapperif (!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 servletsif (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 macthinternalMapExactWrapper(exactWrappers, path, mappingData);// Rule 4b -- Welcome resources processing for prefix matchif (mappingData.wrapper == null) {internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,path, mappingData);}// Rule 4c -- Welcome resources processing// for physical folderif (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 servletif (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 folderchar[] 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 rootif (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寻找wrapperMappedWrapper 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 servletmappingData.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 RequestHost host = request.getHost();if (host == null) {return;}if (request.isAsyncSupported()) {request.setAsyncSupported(host.getPipeline().isAsyncSupported());}// Ask this Host to process this requesthost.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) {//实例化servletservlet = wrapper.allocate();}//...//create filter调用链ApplicationFilterChain filterChain =ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);//...//调用filterfilterChain.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 nullif (servlet == null)return null;// Create and initialize a filter chain objectApplicationFilterChain 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 usefilterChain = new ApplicationFilterChain();}filterChain.setServlet(servlet);filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());// 获取启动时初始化的filterStandardContext 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 mappingsDispatcherType 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 chainfor (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 problemcontinue;}filterChain.addFilter(filterConfig);}//校验servlet name是否匹配filter的servletNames// Add filters that match on servlet name secondfor (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 problemcontinue;}filterChain.addFilter(filterConfig);}// Return the completed filter chainreturn 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 dispatchesif (filterMap.getMatchAllUrlPatterns())return true;if (requestPath == null)return false;// Match on context relative request pathString[] testPaths = filterMap.getURLPatterns();for (int i = 0; i < testPaths.length; i++) {if (matchFiltersURL(testPaths[i], requestPath)) {return true;}}// No matchreturn false;}private static boolean matchFiltersURL(String testPath, String requestPath) {if (testPath == null)return false;// Case 1 - Exact Matchif (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 Matchif (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" Matchreturn 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 nameelse 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();//调用filterfilter.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 lessmaybeSetLastModified(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://github.com/c-rainstorm/blog/blob/master/tomcat/%E8%B0%88%E8%B0%88Tomcat%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.md
https://blog.csdn.net/jiaomingliang/article/details/47414657
https://www.jianshu.com/p/d8a2bc7d3c21
这篇关于Tomcat源码解析:请求处理过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!