tomcat处理一个请求的过程
2017-07-16 22:32
369 查看
首先,tomcat是一个基于组件的服务器,它的构成组件都是可配置的,可以在tomcat目录下conf/server.xml中进行配置。其配置文件结构如下:
tomcat是基于组件的,分层避不可免,对于一个请求的处理,tomcat的链式调用比较长,让我们从接收请求开始说起。
tomcat处理socket请求的IO模型有BIO、NIO、AIO等,后两者IO模型比较复杂,而本文的主要关注点不在这,因此将tomcat配置成BIO模式。
tomcat接收请求的代码在Acceptor类中:
Acceptor 继承了 AbstractEndpoint.Acceptor ,间接实现了Runnable接口,tomcat在运行时将Acceptor运行在一个后台线程内,单独监听socket请求,此线程的调用栈如下:
processSocket()方法如下:
processSocket方法主要工作为将socket请求信息进行封装,然后将一个实现了Runnable接口的并包含socket信息的SocketProcessor对象交给线程池,进行执行,然后Acceptor线程从该方法返回,重新监听端口上的socket请求。
线程池receive到该worker后,取出一个线程处理socket请求,其调用栈如下:
调用栈底部的processor和handler主要处理TCP和HTTP协议的一些细节,CoyoteAdapter实现了对request输入流的编解码工作,并从service中获取顶层Container,将request和response交与容器组件,关键代码如下:
其中,在每一层容器中对请求的处理都运用了责任链模式,即所谓的Pipeline-Value处理模式,pipeline就像一个管道,表示对请求的链式处理过程,其中包含多个value,每个value代表对请求的一次处理,处理完成之后交给next Value进行处理,每层容器都至少有一个Value,这个Value被成为BaseValue,在pipeline中位于最后一个位置,比如Engine容器的BaseValue为StandardEngineValue。每一层的BaseValue会调用下一层容器的pipeline的第一个Value,由此形成一个长链:
到达pipeline长链最后一个value StandardWrapperValue后,会触发另外一个责任链模式:filterChain责任链,也就是我们平常熟悉的在web项目中配置的filter被调用的位置。
StandardWrapperValue调用filterChain代码如下:
ApplicationFilterFactory.getInstance()方法如下:
看到这个单例方法我有点蒙,这不明摆着存在竞态条件么,完全线程不安全的单例工厂 - - ,源码为tomcat7.0,难道这样没问题?
createFilterChain创建filterChain的代码如下:
创建filterChain代码比较简单,首先创建一个FilterChain对象,然后把url对应的servlet放进去,相当于pipeline-value模式中的最后一个BaseValue。之后从ServletContext中拿出所有的filter,然后根据url和servlet name找到符合条件的filter,根据顺序组装到filterChain中。
可能看到这里有些人会想,为什么要为每次请求都创建一个新的FilterChain对象呢,这显得有些不合常理,因为对于tomcat的大部分组件、filter、servlet都是单例的,而且频繁new操作有些消耗资源吧。确实是这样的,但这里将FilterChain做成prototype是有原因的,因为对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter。这里的filter不能像pipeline-value模式那样组装起来,而是依靠filterChain来决定每个url的调用顺序。
创建完filterchain后,StandardWrapperValue就开始调用filterChain的doFilter方法了:
pos为filterchain对象内的一个变量,用来标记当前调用的filter的位置,filter.doFilter(request, response, this),这句代码即调用咱们的filter了,记不记得咱们自己写filter时,如果不直接close outputstream,都会在doFilter最后写一句:filterChain.doFilter(req,res)来回调filterChain,让它继续调用接下来的filter。
在pos == n (filter数组长度)时,也就是filter调用完了,servlet.service(request, response)这一句便是调用我们的servlet了。我们的servlet在处理完请求后,再一步一步按原路返回,将处理结果通过socket写回客户端。
至此,一个请求就被tomcat处理完了。
<Server>顶层类元素:一个配置文件中只能有一个<Server>元素,可包含多个Service。 <Service>顶层类元素:本身不是容器,可包含一个Engine,多个Connector。 <Connector/>连接器类元素:代表通信接口。 <Engine>容器类元素:为特定的Service组件处理所有客户请求,可包含多个Host。engine为顶层Container <Host>容器类元素:为特定的虚拟主机处理所有客户请求,可包含多个Context。 <Context>容器类元素:为特定的Web应用处理所有客户请求。代表一个应用,包含多个Wrapper(封装了servlet) </Context> </Host> </Engine> </Service> </Server>
tomcat是基于组件的,分层避不可免,对于一个请求的处理,tomcat的链式调用比较长,让我们从接收请求开始说起。
tomcat处理socket请求的IO模型有BIO、NIO、AIO等,后两者IO模型比较复杂,而本文的主要关注点不在这,因此将tomcat配置成BIO模式。
tomcat接收请求的代码在Acceptor类中:
/** * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. * JIoEndpoint 的内部类 */ protected class Acceptor extends AbstractEndpoint.Acceptor { @Override public void run() { // Loop until we receive a shutdown command while (running) { //if we have reached max connections, wait countUpOrAwaitConnection(); Socket socket = null; //阻塞住 监听新的socket请求 socket = serverSocketFactory.acceptSocket(serverSocket); // Configure the socket if (running && !paused && setSocketOptions(socket)) { // Hand this socket off to an appropriate processor 首先使用processSocket()简单处理socket if (!processSocket(socket)) { countDownConnection(); // Close socket right away closeSocket(socket); } } else { countDownConnection(); // Close socket right away closeSocket(socket); } } } }
Acceptor 继承了 AbstractEndpoint.Acceptor ,间接实现了Runnable接口,tomcat在运行时将Acceptor运行在一个后台线程内,单独监听socket请求,此线程的调用栈如下:
processSocket()方法如下:
/** * Process a new connection from a new client. Wraps the socket so * keep-alive and other attributes can be tracked and then passes the socket * to the executor for processing. */ protected boolean processSocket(Socket socket) { // Process the request from this socket SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket); wrapper.setKeepAliveLeft(getMaxKeepAliveRequests()); wrapper.setSecure(isSSLEnabled()); // During shutdown, executor may be null - avoid NPE if (!running) { return false; } getExecutor().execute(new SocketProcessor(wrapper)); return true; }
processSocket方法主要工作为将socket请求信息进行封装,然后将一个实现了Runnable接口的并包含socket信息的SocketProcessor对象交给线程池,进行执行,然后Acceptor线程从该方法返回,重新监听端口上的socket请求。
线程池receive到该worker后,取出一个线程处理socket请求,其调用栈如下:
调用栈底部的processor和handler主要处理TCP和HTTP协议的一些细节,CoyoteAdapter实现了对request输入流的编解码工作,并从service中获取顶层Container,将request和response交与容器组件,关键代码如下:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
其中,在每一层容器中对请求的处理都运用了责任链模式,即所谓的Pipeline-Value处理模式,pipeline就像一个管道,表示对请求的链式处理过程,其中包含多个value,每个value代表对请求的一次处理,处理完成之后交给next Value进行处理,每层容器都至少有一个Value,这个Value被成为BaseValue,在pipeline中位于最后一个位置,比如Engine容器的BaseValue为StandardEngineValue。每一层的BaseValue会调用下一层容器的pipeline的第一个Value,由此形成一个长链:
到达pipeline长链最后一个value StandardWrapperValue后,会触发另外一个责任链模式:filterChain责任链,也就是我们平常熟悉的在web项目中配置的filter被调用的位置。
StandardWrapperValue调用filterChain代码如下:
//StandardWrapperValue类 public final void invoke(Request request, Response response){ ... // Create the filter chain for this request ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); ... //开始调用filterChain filterChain.doFilter(request.getRequest(), response.getResponse()); ... }
ApplicationFilterFactory.getInstance()方法如下:
/** * Return the factory instance. */ //ApplicationFilterFactory类 public static ApplicationFilterFactory getInstance() { if (factory == null) { factory = new ApplicationFilterFactory(); } return factory; }
看到这个单例方法我有点蒙,这不明摆着存在竞态条件么,完全线程不安全的单例工厂 - - ,源码为tomcat7.0,难道这样没问题?
createFilterChain创建filterChain的代码如下:
//ApplicationFilterChain类 public ApplicationFilterChain createFilterChain (ServletRequest request, Wrapper wrapper, Servlet servlet) { ApplicationFilterChain filterChain = null; ... filterChain = new ApplicationFilterChain(); ... filterChain.setServlet(servlet); ... // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings String servletName = wrapper.getName(); // Add the relevant path-mapped filters to this filter chain 根据请求url找到对应的filter指针,添加进来 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); } // Add filters that match on servlet name second //如果配置了根据servlet名称过滤,则再寻找一遍filter 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; } ... //addFilter时会对filter指针排重 filterChain.addFilter(filterConfig); } // Return the completed filter chain return (filterChain); }
创建filterChain代码比较简单,首先创建一个FilterChain对象,然后把url对应的servlet放进去,相当于pipeline-value模式中的最后一个BaseValue。之后从ServletContext中拿出所有的filter,然后根据url和servlet name找到符合条件的filter,根据顺序组装到filterChain中。
可能看到这里有些人会想,为什么要为每次请求都创建一个新的FilterChain对象呢,这显得有些不合常理,因为对于tomcat的大部分组件、filter、servlet都是单例的,而且频繁new操作有些消耗资源吧。确实是这样的,但这里将FilterChain做成prototype是有原因的,因为对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter。这里的filter不能像pipeline-value模式那样组装起来,而是依靠filterChain来决定每个url的调用顺序。
创建完filterchain后,StandardWrapperValue就开始调用filterChain的doFilter方法了:
//ApplicationFilterChain类 public void doFilter(ServletRequest request, ServletResponse response){ ... internalDoFilter(request,response); ... } private void internalDoFilter(ServletRequest request, ServletResponse response){ ... // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; filter = filterConfig.getFilter(); filter.doFilter(request, response, this); return; } ... // We fell off the end of the chain -- call the servlet instance servlet.service(request, response); ... }
pos为filterchain对象内的一个变量,用来标记当前调用的filter的位置,filter.doFilter(request, response, this),这句代码即调用咱们的filter了,记不记得咱们自己写filter时,如果不直接close outputstream,都会在doFilter最后写一句:filterChain.doFilter(req,res)来回调filterChain,让它继续调用接下来的filter。
在pos == n (filter数组长度)时,也就是filter调用完了,servlet.service(request, response)这一句便是调用我们的servlet了。我们的servlet在处理完请求后,再一步一步按原路返回,将处理结果通过socket写回客户端。
至此,一个请求就被tomcat处理完了。
相关文章推荐
- Tomcat Server处理一个http请求的过程
- [转]Tomcat处理一个HTTP请求的过程
- Tomcat处理一个HTTP请求的过程
- tomcat Server处理一个http请求的过程
- Tomcat学习(一) - Tomcat结构以及处理一个请求的过程
- Tomcat学习(一) - Tomcat结构以及处理一个请求的过程
- Tomcat如何处理一个HTTP请求的过程?
- Tomcat结构以及处理一个请求的过程
- Tomcat Server处理一个http请求的过程
- Tomcat处理一个http请求的过程
- Tomcat Server组成及处理一个http请求的过程
- Tomcat处理一个HTTP请求的过程
- tomcat原理,一个客户端请求的处理过程
- Tomcat结构以及处理一个请求的过程
- Tomcat Server处理一个http请求的过程
- Tomcat Server处理一个http请求的过程
- Tomcat目录结构及Tomcat Server处理一个http请求的过程
- omcat目录结构及Tomcat Server处理一个http请求的过程
- Tomcat Server处理一个http请求的过程
- 网站开发进阶(四)Tomcat Server处理一个http请求的过程