文章目录
我们都知道,一个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