你的老朋友Tomcat😺(三)请求过程的源码分析

前面两篇文章介绍了Tomcat的总体架构和核心组件,这一篇通过debug源码的方式来分析一个请求在Tomcat中是如何处理的,在默认的NIO模式下分析。

一、添加自定义Servlet

在项目中创建如图所示的文件夹以及文件:

MyServlet和web.xml如下所示:

public class MyServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

PrintWriter pw = resp.getWriter();

pw.println("hello MyServlet");

}

}

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

version="4.0"

metadata-complete="true">

<description>

Servlet and JSP Examples.

</description>

<display-name>Servlet and JSP Examples</display-name>

<request-character-encoding>UTF-8</request-character-encoding>

<servlet>

<servlet-name>myServlet</servlet-name>

<servlet-class>mywebapp.MyServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>myServlet</servlet-name>

<url-pattern>/myServlet</url-pattern>

</servlet-mapping>

</web-app>

接下来接可以访问localhost:8080/mywebapp/myServlet路由debug源码了

二、源码分析

2.1 Connector部分分析

在上一节中我们讲到了连接器中比较重要的组件有Endpoint、Processor、Adapter,其中Endpoint中有负责监听socket连接的组件Acceptor和处理socket请求的组件SocketProcessor。首先看Acceptor,它实现了Runnable接口run()方法如下:

这个方法中比较重要的地方有:

  • endpoint.countUpOrAwaitConnection();它调用的是子组件LimitLatch的countUpOrAwait()方法,而LimitLatch的子类Sync继承了AQS重写了tryAcquireShared()方法限制最大连接数,默认的最大连接数为8*1024;

  • socket = endpoint.serverSocketAccept();启动之后阻塞在这里等待下一次socket连接

  • if (!endpoint.setSocketOptions(socket))处理指定连接,将socket移交给指定处理器


NioEndpoint.setSocketOptions()方法用来设置SocketChannel为非阻塞模式以及其他属性,生成一个NioChannel和NioSocketWrapper对象并设置属性,将SocketChannel、NioSocketWrapper设置为NioChannel的属性,SocketWrapperBase这个包装类是为了屏蔽不同I/O模式下的Channel类的差异,再将它们注册到Poller组件poller.register(channel, socketWrapper);也就是封装一个PollerEvent对象加入Poller组件的Queue里。


// Poller

@Override

public void run() {

// Loop until destroy() is called

while (true) {

boolean hasEvents = false;

try {

if (!close) {

hasEvents = events();

if (wakeupCounter.getAndSet(-1) > 0) {

// If we are here, means we have other stuff to do Do a non blocking select

keyCount = selector.selectNow();

} else {

keyCount = selector.select(selectorTimeout);

}

wakeupCounter.set(0);

}

……

} catch (Throwable x) {

……

continue;

}

// Either we timed out or we woke up, process events first

if (keyCount == 0) {

hasEvents = (hasEvents | events());

}

Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;

// Walk through the collection of ready keys and dispatch any active event.

while (iterator != null && iterator.hasNext()) {

SelectionKey sk = iterator.next();

NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();

// Attachment may be null if another thread has called cancelledKey()

if (socketWrapper == null) {

iterator.remove();

} else {

iterator.remove();

// 处理发生事件的key

processKey(sk, socketWrapper);

}

}

timeout(keyCount,hasEvents);

}

getStopLatch().countDown();

}

// Poller

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {

try {

if (close) {

cancelledKey(sk, socketWrapper);

} elseif (sk.isValid() && socketWrapper != null) {

if (sk.isReadable() || sk.isWritable()) {

……

// Read goes before write

if (sk.isReadable()) {

if (socketWrapper.readOperation != null) {

if (!socketWrapper.readOperation.process()) {

closeSocket = true;

}

} elseif (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {

closeSocket = true;

}

}

……

}

} else {

cancelledKey(sk, socketWrapper);

}

} catch (CancelledKeyException ckx) {

……

}

}

// AbstractEndpoint

public boolean processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch) {

try {

if (socketWrapper == null) {

returnfalse;

}

SocketProcessorBase<S> sc = null;

if (processorCache != null) {

sc = processorCache.pop();

}

if (sc == null) {

sc = createSocketProcessor(socketWrapper, event);

} else {

sc.reset(socketWrapper, event);

}

// debug-tomcat9-print

System.out.println("AbstractEndpoint.processSocket(), event:" + event + ", socketProcessor:" + sc.hashCode());

Executor executor = getExecutor();

if (dispatch && executor != null) {

executor.execute(sc);

} else {

sc.run();

}

} catch (RejectedExecutionException ree) {

……

}

returntrue;

}

// SocketProcessor

@Override

protected void doRun() {

// debug-tomcat9-print

System.out.println("SocketProcessor.doRun(), event:" + event + ", Thread:" + Thread.currentThread().getId() + ", socketProcessor:" + this.hashCode());

NioChannel socket = socketWrapper.getSocket();

Poller poller = NioEndpoint.this.poller;

……

try {

int handshake = -1;

try {

if (socket.isHandshakeComplete()) {

handshake = 0;

} elseif (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||

event == SocketEvent.ERROR) {

handshake = -1;

} else {

handshake = socket.handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE);

event = SocketEvent.OPEN_READ;

}

} catch (IOException x) {

……

}

if (handshake == 0) {

SocketState state = SocketState.OPEN;

if (event == null) {

state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);

} else {

// debug-tomcat9-work-connector

state = getHandler().process(socketWrapper, event);

}

if (state == SocketState.CLOSED) {

poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);

}

} elseif (handshake == -1 ) {

getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);

poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);

} elseif (handshake == SelectionKey.OP_READ){

socketWrapper.registerReadInterest();

} elseif (handshake == SelectionKey.OP_WRITE){

socketWrapper.registerWriteInterest();

}

} catch (CancelledKeyException cx) {

……

} finally {

……

}

}

// AbstractProtocol

@Override

public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {

……

S socket = wrapper.getSocket();

Processor processor = (Processor) wrapper.getCurrentProcessor();

……

try {

if (processor == null) {

……

}

if (processor == null) {

processor = recycledProcessors.pop();

}

if (processor == null) {

processor = getProtocol().createProcessor();

register(processor);

}

processor.setSslSupport(wrapper.getSslSupport(getProtocol().getClientCertProvider()));

wrapper.setCurrentProcessor(processor);

SocketState state = SocketState.CLOSED;

do {

// debug-tomcat9-work-connector

state = processor.process(wrapper, status);

……

} while ( state == SocketState.UPGRADING);

return state;

}

……

}

// AbstractProcessorLight

@Override

public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)

throws IOException {

// debug-tomcat9-work-connector

SocketState state = SocketState.CLOSED;

Iterator<DispatchType> dispatches = null;

do {

……

} elseif (status == SocketEvent.OPEN_READ) {

state = service(socketWrapper);

} elseif (status == SocketEvent.CONNECT_FAIL) {

……

} while (state == SocketState.ASYNC_END ||

dispatches != null && state != SocketState.CLOSED);

return state;

}

// Http11Processor

@Override

public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {

RequestInfo rp = request.getRequestProcessor();

rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

setSocketWrapper(socketWrapper);

……

// debug-tomcat9-work-connector

getAdapter().service(request, response);

……

}

// CoyoteAdapter

@Override

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)

throws Exception {

Request request = (Request) req.getNote(ADAPTER_NOTES);

Response response = (Response) res.getNote(ADAPTER_NOTES);

if (request == null) {

// Create objects

request = connector.createRequest();

request.setCoyoteRequest(req);

response = connector.createResponse();

response.setCoyoteResponse(res);

// Link objects

request.setResponse(response);

response.setRequest(request);

// Set as notes

req.setNote(ADAPTER_NOTES, request);

res.setNote(ADAPTER_NOTES, response);

// Set query string encoding

req.getParameters().setQueryStringCharset(connector.getURICharset());

}

if (connector.getXpoweredBy()) {

response.addHeader("X-Powered-By", POWERED_BY);

}

boolean async = false;

boolean postParseSuccess = false;

req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());

try {

// Parse and set Catalina and configuration specific request parameters

// debug-tomcat9-work

postParseSuccess = postParseRequest(req, request, res, response);

if (postParseSuccess) {

//check valves if we support async

request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());

// Calling the container

// debug-tomcat9-doc 获取Engine调用pipeline链式处理

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

}

……

// 回写request、response

request.finishRequest();

response.finishResponse();

……

}

  1. poller线程不断循环取出events中的事件,进行处理,注册channel到selector上并监听OP_READ事件;selector对有事件发生的SelectionKey调用processKey(SelectionKey sk, NioSocketWrapper socketWrapper)方法;
  2. 对读写事件的key将对应的socketWrapper调用processSocket(SocketWrapperBase<T> socketWrapper, SocketEvent event, boolean dispatch)方法处理;
  3. 根据socketWrapper和event创建一个SocketProcessor对象(其父类SocketProcessorBase实现了Runnable接口)提交给线程池executor执行;
  4. SocketProcessor类重写了doRun()方法,握手完成的socketWrapper且event不为null则调用getHandler().process(socketWrapper, event)也就是ConnectionHandler中的process(SocketWrapperBase<S> wrapper, SocketEvent status)方法,在这个方法中拿到一个Processor(Http11Processor)处理应用层协议相关问题,最后调用到Http11Processor.service(SocketWrapperBase<S> socketWrapper)方法;这个方法中解析了HTTP请求头相关信息并调用了getAdapter().service(request, response)
  5. CoyoteAdapter.service(org.apache.coyote.Request req, org.apache.coyote.Response res)方法将Tomcat的request、response对象转换为Servlet规范的request、response对象;然后调用postParseRequest(req, request, res, response)记录access日志、解析请求URI和后面的参数、后续还有解析session,调用了connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData())处理请求映射 (获取 host, context, wrapper);

2.2 Container部分分析

// Mapper

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();

internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);

}

// Mapper

private final void internalMap(CharChunk host, CharChunk uri,

String version, MappingData mappingData) throws IOException {

if (mappingData.host != null) {

throw new AssertionError();

}

// Virtual host mapping

MappedHost[] hosts = this.hosts;

// debug-tomcat9-doc 从hosts数组中根据主机名找到MappedHost

MappedHost mappedHost = exactFindIgnoreCase(hosts, host);

if (mappedHost == null) {

// 特殊处理后再次寻找

……

}

mappingData.host = mappedHost.object;

if (uri.isNull()) {

return;

}

uri.setLimit(-1);

// Context mapping

ContextList contextList = mappedHost.contextList;

MappedContext[] contexts = contextList.contexts;

……

// 找到context设置到mappingData

mappingData.context = contextVersion.object;

mappingData.contextSlashCount = contextVersion.slashCount;

// Wrapper mapping

if (!contextVersion.isPaused()) {

// debug-tomcat9-doc 从contextVersion的wrappers数组中根据主机名找到MappedWrapper

internalMapWrapper(contextVersion, uri, mappingData);

}

}

// Mapper

// 根据hostName算出在hosts数组中位置下标,找到Host

private static final <T, E extends MapElement<T>> E exactFindIgnoreCase(E[] map, CharChunk name) {

int pos = findIgnoreCase(map, name);

if (pos >= 0) {

E result = map[pos];

if (name.equalsIgnoreCase(result.name)) {

return result;

}

}

return null;

}

// 继续执行CoyoteAdpter中service()方法的connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

// StandardEngineValve

@Override

public final void invoke(Request request, Response response) throws IOException, ServletException {

// 前面request中的mappingData已获取到host信息

Host host = request.getHost();

if (host == null) {

// HTTP 0.9 or HTTP 1.0 request without a host when no default host

// is defined. This is handled by the CoyoteAdapter.

return;

}

if (request.isAsyncSupported()) {

request.setAsyncSupported(host.getPipeline().isAsyncSupported());

}

// Ask this Host to process this request

host.getPipeline().getFirst().invoke(request, response);

}

//

public final void invoke(Request request, Response response)

throws IOException, ServletException {

……

StandardWrapper wrapper = (StandardWrapper) getContainer();

Servlet servlet = null;

Context context = (Context) wrapper.getParent();

……

try {

if (!unavailable) {

// debug-tomcat9-doc 分配servlet处理请求

servlet = wrapper.allocate();

}

} catch (UnavailableException e) {

……

} catch (ServletException e) {

container.getLogger().error(sm.getString("standardWrapper.allocateException",

wrapper.getName()), StandardWrapper.getRootCause(e));

throwable = e;

exception(request, response, e);

} catch (Throwable e) {

……

}

……

// debug-tomcat9-doc 获取请求path

MessageBytes requestPathMB = request.getRequestPathMB();

DispatcherType dispatcherType = DispatcherType.REQUEST;

if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;

request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);

request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB);

// debug-tomcat9-doc 创建filterChain

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

Container container = this.container;

try {

if ((servlet != null) && (filterChain != null)) {

// Swallow output if needed

if (context.getSwallowOutput()) {

try {

SystemLogHandler.startCapture();

if (request.isAsyncDispatching()) {

request.getAsyncContextInternal().doInternalDispatch();

} else {

// debug-tomcat9-doc 开始执行filter链,最后到servlet中

filterChain.doFilter(request.getRequest(), response.getResponse());

}

} finally {

String log = SystemLogHandler.stopCapture();

if (log != null && log.length() > 0) {

context.getLogger().info(log);

}

}

} else {

if (request.isAsyncDispatching()) {

request.getAsyncContextInternal().doInternalDispatch();

} else {

filterChain.doFilter

(request.getRequest(), response.getResponse());

}

}

}

} catch (ClientAbortException | CloseNowException e) {

……

} finally {

// Release the filter chain (if any) for this request

if (filterChain != null) {

filterChain.release();

}

// Deallocate the allocated servlet instance

try {

if (servlet != null) {

wrapper.deallocate(servlet);

}

} catch (Throwable e) {

……

}

……

}

}

  1. 在Mapper组件的map(MessageBytes host, MessageBytes uri, String version, MappingData mappingData)方法中简单处理了host和uri然后调用internalMap();
  2. internalMap()方法根据host和uri找到Host和Context以及Wrapper;(这些映射关系在容器启动的时候都缓存在Mapper组件中了)
  3. 开始调用Tomcat容器, 首先调用StandardEngine容器中的管道PipeLine中的第一个Valve(StandardEngineValve), 传入request与response来处理所有逻辑,之后经过一系列的Valve(StandardHostValve、StandardWrapperValve等)可以理解为容器级别的过滤器
  4. StandardWrapperValve的invoke()方法中进行servlet的分配,创建请求的ApplicationFilterChain,对象包装请求的Servlet对象及一些过滤器Filter对象,执行 filterChain 链, 在链的末尾是servlet调用service(request, response)方法
  5. 通过request.finishRequest与response.finishResponse(发送OutputBuffer中的数据到浏览器)完成整个请求

三、注意问题

  1. 如果是用浏览器访问localhost:8080/mywebapp/myServlet来进行调试,tomcat会接收到两个http请求,有一个是/favicon.ico获取浏览器标签图标的,这样会打扰到正常的debug。
  2. 使用postman调试也会收到两个tcp连接,具体不清楚第一个tcp连接的作用,也会干扰connector部分的debug。
  3. 推荐使用telnet发送http请求来进行调试。
  4. debug的断点处我在对应位置加了// debug-tomcat9- 格式的注释,上传至GitHub了。

有些地方写的比较模糊,只是讲了个整体脉络,能力有限😑

以上是 你的老朋友Tomcat&#x1f63a;(三)请求过程的源码分析 的全部内容, 来源链接: utcz.com/a/33519.html

回到顶部