Tomcat源码目录下的webapps中包含有一些简单的servlet例子,在此通过分析hello world的servlet请求过程来看tomcat是如何执行的。
启动tomcat后,访问http://localhost:8080/examples/servlets/servlet/HelloWorldExample,以debug模式单步调试,可以得到流程图。
启动过程主要分为3个阶段:
1、tomcat如何捕获servlet请求。
2、接收到请求后如何在各容器中流动,最后到达最终处理地点。
3、处理完成后如何返回。
启动过程主要分为3个阶段:
1、tomcat如何捕获servlet请求。
2、接收到请求后如何在各容器中流动,最后到达最终处理地点。
3、处理完成后如何返回。
下面逐一进行分析。
1、第一阶段:捕获请求
1) JIoEndpoint的内部类Acceptor,接受浏览器的socket请求。
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
//...
while (running) {
//...
try {
// 接受发送过来的请求
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
//...
}
//...
if (!processSocket(socket)) {
countDownConnection();
//关闭连接
closeSocket(socket);
}
//...
}
//...
}
}
2)调用SocketProcessor的 processSocket方法处理请求,请求会被传到AbstractProtocol内部类AbstractConnectionHandler的process方法去处理。此方法中会创建Http11Processor的对象processor。
3) 使用创建的processor的process方法来处理请求。此方法中将请求解析成Request和Response对象。又会调用CoyoteAdapter来进行适配处理,org.apache.coyote.Request对象被转成了org.apache.catalina.connector.Request对象,后一类型的对象才是在Tomcat容器流转时真正传递的对象。
postParseRequest方法中,通过map方法的设置request的成员变量mappingData的成员变量host、context、warp信息,接着从mappingData中取出context和wrapper,直接设置到request对象的成员变量context、wrapper中。
Tomcat容器启动过程中会将用到的Host、Context、Wrapper组件维护到与Connector相关的Mapper对象里,这样容器在接收到请求之后,才可以根据请求的URL等信息匹配到具体的host、context、wrapper。
方法的最后,将org.apache.catalina.connector.Request对象和
org.apache.catalina.connector.Response对象,交给StandardEngine 做核心的处理工作了。
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
2、第二阶段:在各容器中流动
Request在各个容器里面的穿梭大致是这样一种方式:
每个容器里面都有一个管道(pipline), 专门用来传送Request用的。管道里面又有好几个阀门(valve), 专门用来过滤Request用的。
在管道的低部通常都会放上一个默认的阀们。 这个阀们至少会做一件事情,就是把Request交给子容器。
1)StandardEngine 的pipeline里面放的是:StandardEnginValve。设置是否支持异步请求后,直接将请求交由StandardHost的pipeline去处理。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
Host host = request.getHost();
if (host == null) {
//...
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
2) StandardHost 的pipline里面放的第一个valve是:AccessLogValve
public void invoke(Request request, Response response) throws IOException,
ServletException {
getNext().invoke(request, response);
}
此方法中直接调用下一个valve:ErrorReportValve的invoke方法,其中又调用下一个valve:StandardHostValve的invoke方法。StandardHostValve的invoke方法中,主要工作是:
根据Request来确定哪个Context来处理(Context其实就是webapp, 比如http://localhost:8080/examples/servlets/servlet/HelloWorldExample这里examples就是Context!),并把这个Context的classloader赋值给当前线程,然后转到context的pipeline。这样request就只看得见当前的context下面的classes和jar, 而看不见tomcat服务本身的类, Engine,Valve等。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
//...
}
// Bind the context CL to the current thread
if( context.getLoader() != null ) {
if (Globals.IS_SECURITY_ENABLED) {
//...
} else {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
//...
if (!asyncAtStart || asyncDispatching) {
context.getPipeline().getFirst().invoke(request, response);//转context的pipeline
}
//...
}
3) context的pipeline中的第一个valve是FormAuthenticator,验证或更新session等操作之后,转到StandardContextValve,此方法中将request交给子容器StandardWrapper的pipeline。
4) Wrapper的pipeline的第一个valve是StandardWrapperValve。主要工作是:1、按类名HelloWorldExample,创建一个HttpServlet实例,用于处理请求。2、创建filter chain。3、filterChain.doFilter(request,response),请求通过一系列filter之后,调用HttpServlet.service(request, response),此方法中根据http请求的方法判断调用具体类(此例中为HelloWorldExample)中的doPost,doPut,doGet等。
3、第三阶段:处理完成后续工作
1)ApplicationFilterChain重置,释放分配的servlet实例
2)重置StandardHost的classloader
Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());、
3) 如果有错误,处理错误信息。
4) 打印日志。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者何欢授权发布。