前言
前面介绍过,tomcat中的组件主要分两种,connector和containner,connector主要负责tomcat与客户端通讯,containner主要处理业务;这里大概提一下,connector启动时,会启动相关的Endpoint,端口的绑定和监听,主要是由Endpoint来执行的。
流转图
组件图
第一章里发布的架构图,也可以对照看一下
什么是Valve
Valve是与特定容器关联的请求处理组件,一系列的Valve相互关联成一个Pipeline,通过invoke()方法来处理数据。下面是官方注解
A Valve is a request processing component associated with a particular Container. A series of Valves are generally associated with each other into a Pipeline. The detailed contract for a Valve is included in the description of the invoke() method below.
HISTORICAL NOTE: The "Valve" name was assigned to this concept because a valve is what you use in a real world pipeline to control and/or modify flows through it.
步骤
请求的处理主要分为4个步骤:
- 监听服务器端口,读取来自客户端的请求;
- 将请求数据按照指定的协议进行解析;
- 根据请求地址匹配正确的容器进行处理;
- 返回客户响应;
下面一一来介绍,文章研究的版本是tomcat9.0.36
接收请求
根据请求中的端口地址来选择连接器,比如http://localhost:8080/xxx,那么就会选择监听8080端口的连接器进行接收处理。
当前版本默认使用的是非阻塞线程模型,分为接收者和执行者,接收者是单个线程,启动之后一直监听;执行者是java线程池,执行具体业务请求;
Nio的接收者是NioEndpoint,一个endpoint中,主要做四件事情:
public abstract void bind() throws Exception;
public abstract void unbind() throws Exception;
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;
AbstractEndpoint.startAcceptorThread,启动以后就一直监听
protected void startAcceptorThread() {
acceptor = new Acceptor<>(this);
String threadName = getName() + "-Acceptor";
acceptor.setThreadName(threadName);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
Acceptor类实现了Runnable中,run方法中有个while死循环,一直等待客户端网络事件;
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (endpoint.isRunning()) {
// ... 忽略一些校验代码
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
endpoint.countUpOrAwaitConnection();
U socket = null;
try {
socket = endpoint.serverSocketAccept();
} catch (Exception ioe) {
// We didn't get a socket
endpoint.countDownConnection();
}
// ... 忽略后置处理
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
state = AcceptorState.ENDED;
}
解析协议
将请求数据按照指定的协议进行解析;
Http11Processor.service()
@Override
public SocketState service(SocketWrapperBase<?> socketWrapper)
try {
// 解析协议数据
prepareRequest();
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
// 跳到adapter进行处理
getAdapter().service(request, response);
if(keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
} catch (InterruptedIOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
}
}
匹配容器
根据请求地址匹配正确的容器进行处理;
CoyoteAdapter.service方法,处理请求并返回,接收httpServlet,向下传递时,发送httpServletServlet。
这一步拿到Service中配置的Engine容器。
// 调用容器【container】
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
这里需要介绍一下tomcat中使用到的责任链模式
责任链
tomcat中最基本的元素,集合在pipeline中,每次请求都会执行,类似于filter,在源代码中,预定义了一些valve,access日志也是由valve输出,具体见【org.apache.catalina.valves.AccessLogValve】;
CoyoteAdapter -> StandardEngineValve -> StandardHostValve -> StandardContextValve -> StandardWrapperValve
在StandardWrapperValve的allocate()方法里,会分配一个servlet进行处理请求,默认是抽象类HttpServlet。
可以看看StandardEngineValve是如何调用StandardHostValve的
public final void invoke(Request request, Response response) {
// 读取请求中附带的host信息
Host host = request.getHost();
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// 调用StandardHostValue实例,执行下一步
host.getPipeline().getFirst().invoke(request, response);
}
代码中的【host.getPipeline().getFirst()】,返回的就是StandardHostValve实例。
响应
spring mvc框架中,FrameworkServlet类继承了HttpServlet,进行分发处理,组织业务数据,返回客户端请求;