Overview

  • Connector 启动以后会启动一组线程用于不同阶段的请求处理过程。
  1. Acceptor 线程组。用于接受新连接,并将新连接封装一下,选择一个 Poller 将新连接添加到 Poller 的事件队列中。
  2. Poller 线程组。用于监听 Socket 事件,当 Socket 可读或可写等等时,将 Socket 封装一下添加到 worker 线程池的任务队列中。
  3. worker 线程组。用于对请求进行处理,包括分析请求报文并创建 Request 对象,调用容器的 pipeline 进行处理。
  • AcceptorPollerworker 所在的 ThreadPoolExecutor 都维护在 NioEndpoint 中。

Connector Init and Start

  1. initServerSocket(),通过 ServerSocketChannel.open() 打开一个 ServerSocket,默认绑定到 8080 端口,默认的连接等待队列长度是 100, 当超过 100 个时会拒绝服务。我们可以通过配置 conf/server.xml 中 Connector 的 acceptCount 属性对其进行定制。
  2. createExecutor() 用于创建 Worker 线程池。默认会启动 10 个 Worker 线程,Tomcat 处理请求过程中,Woker 最多不超过 200 个。我们可以通过配置 conf/server.xml 中 Connector 的 minSpareThreads 和 maxThreads 对这两个属性进行定制。
  3. Pollor 用于检测已就绪的 Socket。 默认最多不超过 2 个,Math.min(2,Runtime.getRuntime().availableProcessors());。我们可以通过配置 pollerThreadCount 来定制。
  4. Acceptor 用于接受新连接。默认是 1 个。我们可以通过配置 acceptorThreadCount 对其进行定制。

Requtst Process

Acceptor

  1. Acceptor 在启动后会阻塞在 ServerSocketChannel.accept(); 方法处,当有新连接到达时,该方法返回一个 SocketChannel
  2. 配置完 Socket 以后将 Socket 封装到 NioChannel 中,并注册到 Poller,值的一提的是,我们一开始就启动了多个 Poller 线程,注册的时候,连接是公平的分配到每个 Poller 的。NioEndpoint 维护了一个 Poller 数组,当一个连接分配给 pollers[index] 时,下一个连接就会分配给 pollers[(index+1)%pollers.length].
  3. addEvent() 方法会将 Socket 添加到该 Poller 的 PollerEvent 队列中。到此 Acceptor 的任务就完成了。

Poller

  1. selector.select(1000)。当 Poller 启动后因为 selector 中并没有已注册的 Channel,所以当执行到该方法时只能阻塞。所有的 Poller 共用一个 Selector,其实现类是 sun.nio.ch.EPollSelectorImpl
  2. events() 方法会将通过 addEvent() 方法添加到事件队列中的 Socket 注册到 EPollSelectorImpl,当 Socket 可读时,Poller 才对其进行处理
  3. createSocketProcessor() 方法将 Socket 封装到 SocketProcessor 中,SocketProcessor 实现了 Runnable 接口。worker 线程通过调用其 run() 方法来对 Socket 进行处理。
  4. execute(SocketProcessor) 方法将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。

Worker

  • worker 线程被创建以后就执行 ThreadPoolExecutor 的 runWorker() 方法,试图从 workQueue 中取待处理任务,但是一开始 workQueue 是空的,所以 worker 线程会阻塞在 workQueue.take() 方法。
  • 当新任务添加到 workQueue后,workQueue.take() 方法会返回一个 Runnable,通常是 SocketProcessor,然后 worker 线程调用 SocketProcessor 的 run() 方法对 Socket 进行处理。
  • createProcessor() 会创建一个 Http11Processor, 它用来解析 Socket,将 Socket 中的内容封装到 Request 中。注意这个 Request 是临时使用的一个类,它的全类名是 org.apache.coyote.Request
  • postParseRequest() 方法封装一下 Request,并处理一下映射关系(从 URL 映射到相应的 HostContextWrapper)。
  1. CoyoteAdapter 将 Rquest 提交给 Container 处理之前,并将 org.apache.coyote.Request 封装到 org.apache.catalina.connector.Request,传递给 Container 处理的 Request 是 org.apache.catalina.connector.Request
  2. connector.getService().getMapper().map(),用来在 Mapper 中查询 URL 的映射关系。映射关系会保留到 org.apache.catalina.connector.Request 中,Container 处理阶段 request.getHost() 是使用的就是这个阶段查询到的映射主机,以此类推 request.getContext()request.getWrapper() 都是。
  • connector.getService().getContainer().getPipeline().getFirst().invoke() 会将请求传递到 Container 处理,当然了 Container 处理也是在 Worker 线程中执行的,但是这是一个相对独立的模块,所以单独分出来一节。

Container

  • 需要注意的是,基本上每一个容器的 StandardPipeline 上都会有多个已注册的 Valve,我们只关注每个容器的 Basic Valve。其他 Valve 都是在 Basic Valve 前执行。
  • request.getHost().getPipeline().getFirst().invoke() 先获取对应的 StandardHost,并执行其 pipeline。
  • request.getContext().getPipeline().getFirst().invoke() 先获取对应的 StandardContext,并执行其 pipeline。
  • request.getWrapper().getPipeline().getFirst().invoke() 先获取对应的 StandardWrapper,并执行其 pipeline。
  • 最值得说的就是 StandardWrapper 的 Basic Valve,StandardWrapperValve
  1. allocate() 用来加载并初始化 Servlet,值的一提的是 Servlet 并不都是单例的,当 Servlet 实现了 SingleThreadModel 接口后,StandardWrapper 会维护一组 Servlet 实例,这是享元模式。当然了 SingleThreadModel在 Servlet 2.4 以后就弃用了。
  2. createFilterChain() 方法会从 StandardContext 中获取到所有的过滤器,然后将匹配 Request URL 的所有过滤器挑选出来添加到 filterChain 中。
  3. doFilter() 执行过滤链,当所有的过滤器都执行完毕后调用 Servlet 的 service() 方法。

Tomcat 请求处理流程详解的更多相关文章

  1. Java Struts2 的请求处理流程详解

    一.Struts2的处理流程: 客户端产生一个HttpServletRequest的请求,该请求被提交到一系列的标准过滤器(Filter)组建链中(如ActionContextCleanUp:它主要是 ...

  2. Spring mvc请求处理流程详解(一)之视图解析

      本文链接:https://blog.csdn.net/lchpersonal521/article/details/53112728 前言 Spring mvc框架相信很多人都很熟悉了,关于这方面 ...

  3. tomcat常用配置详解和优化方法

    tomcat常用配置详解和优化方法 参考: http://blog.csdn.net/zj52hm/article/details/51980194 http://blog.csdn.net/wuli ...

  4. 解析Tomcat之HttpServlet详解

    解析Tomcat之HttpServlet详解 Servlet的框架是 由两个Java包组成:javax.servlet和javax.servlet.http. 在javax.servlet包中定义了所 ...

  5. C++的性能C#的产能?! - .Net Native 系列《二》:.NET Native开发流程详解

    之前一文<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥> 获得很多朋友支持和鼓励,也更让我坚定做这项技术的推广者,希望能让更多的朋友了解这项技术,于是先从官方 ...

  6. [nRF51822] 5、 霸屏了——详解nRF51 SDK中的GPIOTE(从GPIO电平变化到产生中断事件的流程详解)

    :由于在大多数情况下GPIO的状态变化都会触发应用程序执行一些动作.为了方便nRF51官方把该流程封装成了GPIOTE,全称:The GPIO Tasks and Events (GPIOTE) . ...

  7. 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

    本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...

  8. tomcat的配置详解:[1]tomcat绑定域名

    转自:http://jingyan.baidu.com/article/7e440953dc096e2fc0e2ef1a.html tomcat的配置详解:[1]tomcat绑定域名分步阅读 在jav ...

  9. Linux下tomcat的安装详解

    Linux下tomcat的安装详解 来源: ChinaUnix博客 日期: 2007.01.21 22:59 (共有0条评论) 我要评论 一,安装前的准备:1,Linux版本:我的是企业版.(至于红帽 ...

随机推荐

  1. 谈谈语音通信中的各种tone

    今天谈的这个主题(tone)存在于我们的日常打电话过程中.先举两个场景:1,你拿起固话话筒准备打电话,按电话号码前先从话筒里听到"嗡"的连续音,这叫dial tone(拨号音,表示 ...

  2. CodeForces 820C

    题意略. 这道题目的出题者竟然被hack了!? 我的思路是:在游戏开始时,为了尽量少地用字母,我应该尽量选取计算机输出的前a个字母中已经使用过的字母.但是为了使电脑也尽量少用字母,我添加的这b个字母应 ...

  3. Android 文件下载三种基本方式

    一.自己封装URLConnection 连接请求类 public void downloadFile1() { try{ //下载路径,如果路径无效了,可换成你的下载路径 String url = & ...

  4. selenium自动化测试配置工具整理

    firefox浏览器历史版本 网址通道:http://ftp.mozilla.org/pub/firefox/releases/ chromedriver历史版本 网址通道:http://chrome ...

  5. C#委托与事件--简单笔记

    委托 简单记录点东西 适合似懂非懂的朋友看看 委托类型用来定义和响应应用程序中的回调. 借此可以设计各种有面向对象特性的代码模式.下面要说的事件在我看来就是委托的一种实现,再深一步讲,利用委托加事件, ...

  6. hibernate学习(六) flush()和clean()区别和使用

    session.flush()是强制和数据库同步 session.clean()是清除session中的缓存 对于批量数据插入的时候优化:减少cpu和内存(缓存)占用量 @Test public vo ...

  7. hihoCoder 1015 KMP算法

    题意:经典字符串匹配算法.给定原串和模式串,求模式串在原串中出现的次数.算法讲解 AC代码 #include <cstdio> #include <cmath> #includ ...

  8. Zookeeper笔记1-CAP/BASE理论

    分布式系统八大谬论: 1.网络相当可靠 2.延迟为0 3.传输带宽是无限的 4.网络相当安全. 5.拓扑结构不会改变 6.必须要有一名管理员 7.传输成本为0 8.网络同质化. 分布式最常出现的问题: ...

  9. Hybrid APP 架构设计思路

    关于Hybrid模式开发app的好处,网络上已有很多文章阐述了,这里不展开. 本文将从以下几个方面阐述Hybrid app架构设计的一些经验和思考. 原文及讨论请到 github issue 通讯 作 ...

  10. (三十)java多线程一

    我们通常在电脑中打开的应用称作进程,一个应用就是一个进程,而一个进程里边一般包含多个线程. 系统要为每一个进程分配独立的内存空间,而进程里的多个线程共用这些内存. 我们通常所写的main方法就是一个线 ...