Tomcat源码解析:请求处理过程

2023-12-22 15:18

本文主要是介绍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;
}
  1. Acceptor 在启动后会阻塞在 endpoint.serverSocketAccept(); 方法处,当有新连接到达时,该方法返回一个 SocketChannel。这里的endpointNioEndpoint
protected SocketChannel serverSocketAccept() throws Exception {return serverSock.accept();
}
  1. 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 添加到该PollerPollerEvent队列中。

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);}}
}
  1. 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);} }
}
  1. 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;
}
  1. 创建socket数据处理器

**NioEndpoint#createSocketProcessor **

protected SocketProcessorBase<NioChannel> createSocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {return new SocketProcessor(socketWrapper, event);
}
  1. 启动线程处理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有两个实现类,AbstractHttp11ProtocolAbstractAjpProtocol,这里是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//...}
}
  1. 解析请求header。
  2. 指定request body的读取filter。
  3. 匹配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);}//...
}
  1. 解析request并匹配servlet
  2. 调用匹配成功的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);}}

这里主要分为两步进行匹配。

  1. 首先匹配context.name,匹配成功后设置到mappingData.contextPath
  2. 匹配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);
}

匹配规则:

  1. 匹配exact servlet,即项目里真实的servlet。
  2. 通用匹配,
  3. 扩展匹配,tomcat容器web.xml默认的servlet
  4. reources匹配,即欢迎页面等
  5. 匹配未配置的扩展名映射,如index.jsf,index.do。
  6. 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.namematchType设置到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());}
}
  1. 实例化servlet。
  2. 创建filter调用链。
  3. 调用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是如何处理的。我们再来做一个总体的总结:

  1. 用户浏览器发送请求,请求会发送到对应的Connector监听的Socket端口。
  2. Connector从Socket流中获取数据,然后根据Http协议将其解析为Request和Reponse对象。
  3. 找到Request对象对应的Host,Context,Wrapper。
  4. 调用filter链。
  5. 调用最终的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源码解析:请求处理过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/524448

相关文章

IDEA与JDK、Maven安装配置完整步骤解析

《IDEA与JDK、Maven安装配置完整步骤解析》:本文主要介绍如何安装和配置IDE(IntelliJIDEA),包括IDE的安装步骤、JDK的下载与配置、Maven的安装与配置,以及如何在I... 目录1. IDE安装步骤2.配置操作步骤3. JDK配置下载JDK配置JDK环境变量4. Maven配置下

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

Python中配置文件的全面解析与使用

《Python中配置文件的全面解析与使用》在Python开发中,配置文件扮演着举足轻重的角色,它们允许开发者在不修改代码的情况下调整应用程序的行为,下面我们就来看看常见Python配置文件格式的使用吧... 目录一、INI配置文件二、YAML配置文件三、jsON配置文件四、TOML配置文件五、XML配置文件

Spring中@Lazy注解的使用技巧与实例解析

《Spring中@Lazy注解的使用技巧与实例解析》@Lazy注解在Spring框架中用于延迟Bean的初始化,优化应用启动性能,它不仅适用于@Bean和@Component,还可以用于注入点,通过将... 目录一、@Lazy注解的作用(一)延迟Bean的初始化(二)与@Autowired结合使用二、实例解

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查