目标
分析tomcat处理一次http请求的nio过程模式,因为bio比较常见,网上好多分析资料可参考。
分析
上一节,我们已经分析了tomcat端启动流程,最后Connector中启动Endpoint进行端口监听,然后在mapperListener中存了相关各容器的映射关系,最后pipeline作为容器的调用管道链。按照这个流程,我们分析调用源码,进行debug,看下最终我的nio调用时序图:
简单语言描述下:
- 请求进入NioEndPoint的内部Acceptor接收请求,根据nio的异步处理机制,socket会作为一个pollerEvent事件存入队列,poller会轮询通过select进行选择可读事件
protected class Acceptor implements Runnable {
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
。。。。。。。。
Thread.sleep(1000);
。。。。。。。
//每次用完一个新建
SocketChannel socket = null;
socket = serverSock.accept();
..............
//此处去注册任务了,然后关闭此socket
if (!setSocketOptions(socket)) {
try {
socket.socket().close();
socket.close();
。。。。。。。。。。。。。。
}//run
}
对应的setSocketOptions方法
protected boolean setSocketOptions(SocketChannel socket) {
//获取NioChannel,没有空闲的则新建一个
NioChannel channel = nioChannels.poll();
。。。。。。。。。。
//注册poller事件任务
getPoller0().register(channel);
。。。。。。。
return true;
}
- selector找到待处理事件后开启异步线程调用SocketProcessor进行处理,SocketProcessor找到其对应的协议处理类封装request,最后通过CoyoteAdapter进行适配处理
看下Poller中监听run代码
public void run() {
// Loop until we receive a shutdown command
while (running) {
try {
// Loop if endpoint is paused
while (paused && (!close) ) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
}
//判断是否有待处理任务
boolean hasEvents = events();
//通过nio的selector选择可读事件,进行处理
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()) {
//处理此key
processKey(sk, attachment);
}
然后进入processKey处理数据,此方法最终调用processSocket(channel, null, true)方法
public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
try {
KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
attachment.setCometNotify(false); //will get reset upon next reg
SocketProcessor sc = processorCache.poll();
if ( sc == null ) sc = new SocketProcessor(socket,status);
else sc.reset(socket,status);
//获取SocketProcessor处理器,如果配置了getExecutor则在异步线程中进行处理,否则直接处理
if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc);
else sc.run();
} catch (RejectedExecutionException rx) {
log.warn("Socket processing request was rejected for:"+socket,rx);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
在SocketProcessor的run方法中,核心部分我们看到
state = (status==null)?handler.process(socket):handler.event(socket,status);
这个handler为Http11NioProtocol,根据我们配置的协议形成,看下其process方法,最终找到其对应方法SocketState state = processor.process(socket);
,此时processor为协议对应的处理器Http11NioProcessor,简化此方法
public SocketState process(NioChannel socket)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
。。。。。。。。
//处理request数据,session,cookie等信息都在此时进行处理
prepareRequest();
//适配找到对应容器业务
adapter.service(request, response);
。。。。。。
}
}
- CoyoteAdapter首先会解析request,最后调用pipeline调用链进入container
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);
//处理request和response信息
.............
//此处会调用pipeline逐级调用进入engine、host、context、wrapper connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
...............
//成功后,返回信息
response.finishResponse();
if (postParseSuccess) {
// Log only if processing was invoked.
// If postParseRequest() failed, it has already logged it.
((Context) request.getMappingData().context).logAccess(
request, response,
System.currentTimeMillis() - req.getStartTime(),
false);
}
req.action(ActionCode.POST_REQUEST , null);
}
。。。。。。。。。
}
}
- 在pipeline层层传递下最后进入StandardWrapperValve找到最终的servlet,匹配path对应的filter,包装filer链,调用filter的doFilter方法,最后进入servlet执行业务
public final void invoke(Request request, Response response)
throws IOException, ServletException {
。。。。。
//找到对应的servlet
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
。。。。。。。。。
//形成过滤器调用链
ApplicationFilterFactory factory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
。。。。。。。
}
- servlet执行完后,CoyoteAdapter会调用finishResponse方法关闭输出流,返回客户端。
知识点:
tomcat中nio的实现,加深对nio概念理解
适配器模式进行接口适配
3.pipeline和filter等责任链模式的使用
参考资料:https://blog.csdn.net/sunyunjie361/article/details/60126398
目录: tomcat 源码学习系列
上一篇: tomcat启动源码分析(二)--入口代码calatina启动介绍
下一篇: tomcat启动源码分析(三)--http请求nio处理