Tomcat 请求处理流程详解
Overview

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

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

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

selector.select(1000)。当Poller启动后因为 selector 中并没有已注册的Channel,所以当执行到该方法时只能阻塞。所有的Poller共用一个 Selector,其实现类是sun.nio.ch.EPollSelectorImplevents()方法会将通过addEvent()方法添加到事件队列中的 Socket 注册到EPollSelectorImpl,当 Socket 可读时,Poller才对其进行处理createSocketProcessor()方法将 Socket 封装到SocketProcessor中,SocketProcessor实现了Runnable接口。worker线程通过调用其run()方法来对 Socket 进行处理。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 映射到相应的Host、Context、Wrapper)。
CoyoteAdapter将 Rquest 提交给Container处理之前,并将org.apache.coyote.Request封装到org.apache.catalina.connector.Request,传递给Container处理的 Request 是org.apache.catalina.connector.Request。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
allocate()用来加载并初始化Servlet,值的一提的是 Servlet 并不都是单例的,当 Servlet 实现了SingleThreadModel接口后,StandardWrapper会维护一组 Servlet 实例,这是享元模式。当然了SingleThreadModel在 Servlet 2.4 以后就弃用了。createFilterChain()方法会从StandardContext中获取到所有的过滤器,然后将匹配 Request URL 的所有过滤器挑选出来添加到filterChain中。doFilter()执行过滤链,当所有的过滤器都执行完毕后调用 Servlet 的service()方法。
Tomcat 请求处理流程详解的更多相关文章
- Java Struts2 的请求处理流程详解
一.Struts2的处理流程: 客户端产生一个HttpServletRequest的请求,该请求被提交到一系列的标准过滤器(Filter)组建链中(如ActionContextCleanUp:它主要是 ...
- Spring mvc请求处理流程详解(一)之视图解析
本文链接:https://blog.csdn.net/lchpersonal521/article/details/53112728 前言 Spring mvc框架相信很多人都很熟悉了,关于这方面 ...
- tomcat常用配置详解和优化方法
tomcat常用配置详解和优化方法 参考: http://blog.csdn.net/zj52hm/article/details/51980194 http://blog.csdn.net/wuli ...
- 解析Tomcat之HttpServlet详解
解析Tomcat之HttpServlet详解 Servlet的框架是 由两个Java包组成:javax.servlet和javax.servlet.http. 在javax.servlet包中定义了所 ...
- C++的性能C#的产能?! - .Net Native 系列《二》:.NET Native开发流程详解
之前一文<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥> 获得很多朋友支持和鼓励,也更让我坚定做这项技术的推广者,希望能让更多的朋友了解这项技术,于是先从官方 ...
- [nRF51822] 5、 霸屏了——详解nRF51 SDK中的GPIOTE(从GPIO电平变化到产生中断事件的流程详解)
:由于在大多数情况下GPIO的状态变化都会触发应用程序执行一些动作.为了方便nRF51官方把该流程封装成了GPIOTE,全称:The GPIO Tasks and Events (GPIOTE) . ...
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...
- tomcat的配置详解:[1]tomcat绑定域名
转自:http://jingyan.baidu.com/article/7e440953dc096e2fc0e2ef1a.html tomcat的配置详解:[1]tomcat绑定域名分步阅读 在jav ...
- Linux下tomcat的安装详解
Linux下tomcat的安装详解 来源: ChinaUnix博客 日期: 2007.01.21 22:59 (共有0条评论) 我要评论 一,安装前的准备:1,Linux版本:我的是企业版.(至于红帽 ...
随机推荐
- Centos7新功能
Centos7 单用户模式 centos7里不再有0-6启动级别,而是4个target graphical.target 多人模式,支持图形和命令行两种登录,对应之前的3,5级别 mul ...
- Selenium常用API用法示例集----下拉框、文本域及富文本框、弹窗、JS、frame、文件上传和下载
元素识别方法.一组元素定位.鼠标操作.多窗口处理.下拉框.文本域及富文本框.弹窗.JS.frame.文件上传和下载 元素识别方法: driver.find_element_by_id() driver ...
- [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio
<<返回目录 Visual Studio vs虽然不是全宇宙唯一的IDE,但它是.net开发人员最常用的开发工具.它自带一个性能分析工具,你可以使用它来做开发,不同的vs版本在工具上会略有 ...
- linux中权限对文件和目录的作用
chmod 755 a.txt 文件: r:读取文件内容(cat more head tail) w:编辑,新增,修改文件的内容(vi,echo) 不包括删除文件:原因是只能对文件内容进行修改,而在l ...
- Ubuntu忘记root密码怎么办?
http://www.linuxidc.com/Linux/2016-05/131256.htm
- PHP如何防止XSS攻击
PHP防止XSS跨站脚本攻击的方法:是针对非法的HTML代码包括单双引号等,使用htmlspecialchars()函数 . 在使用htmlspecialchars()函数的时候注意第二个参数, 直接 ...
- 剑指offer第三天
21.栈的压入.弹出序列 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3, ...
- hdu 2048 递推&&错排
直接贴出递推公式: cnt[n]=(i-1)*(cnt[n-1]+cnt[n-2]); 数组保存的是失败的种数 AC代码: #include<cstdio> const int maxn= ...
- CodeForces-749A
要求组成n的素数最多,根据n的奇偶讨论:如果n是偶数,直接打印n/2个数字'2'就可以了:如果n是基数,则先打印一个'3',再打印(n-3)/2个数字'2'就可以了. AC代码: #include&l ...
- Scrum方法论
产品负责人: 代表客户或未来游戏玩家.产品负责人需要确保所有有趣的功能都能在游戏中实现,还负责对游戏完整观感的理解. Scrum主管: 代表理性思维.需要主持每日Scrum会议,并确保每个人都在执行任 ...