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版本:我的是企业版.(至于红帽 ...
随机推荐
- ng-csv 异步数据下载
相信很多码友遇到一个很坑的问题吧,就是使用ng-csv 的时候 lazy-load="true" 设置为true 还是 会下载0行数据 var getArray= functio ...
- Python3实现QQ机器人自动爬取百度文库的搜索结果并发送给好友(主要是爬虫)
一.效果如下: 二.运行环境: win10系统:python3:PyCharm 三.QQ机器人用的是qqbot模块 用pip安装命令是: pip install qqbot (前提需要有request ...
- Java经典编程题50道之二十五
一个5位数,判断它是不是回文数.即12321是回文数,个位与万位相同,十位与千位相同. public class Example25 { public static void main(Stri ...
- 使用tdload工具将本地数据导入到Teradata数据库中
想把本地的数据文件(比如txt.csv)中的数据导入到Teradata虚拟机中的表中.既可以使用Teradata Assistant中的import功能,也可以使用fastload导入,前者的缺点是一 ...
- RegExp正则表达式规则以及常用正则表达式
html,body { font-family: "SF UI Display", ".PingFang SC", "PingFang SC" ...
- 如何通过 ZAZ-020 电容指纹模块采集指纹信息?
#ifndef _PROTOCOL_H_ #define _PROTOCOL_H_ ///////////////////错误返回码//////////////////// #define PS_OK ...
- POJ - 3984 bfs [kuangbin带你飞]专题一
bfs搜索过程中将路径保存下即可. AC代码 #include<cstdio> #include<cstring> #include<algorithm> #inc ...
- filebeat -> logstash -> elasticsearch -> kibana ELK 日志收集搭建
Filebeat 安装参考 http://blog.csdn.net/kk185800961/article/details/54579376 elasticsearch 安装参考http://blo ...
- ubuntu追加磁盘空间
在用wubi安装的时候,按默认的是20G空间,明显不够用,从Windows上追加空间 首先用win7自带的磁盘分区工具,从任意一个空余空间较多的磁盘划出一块新分区(无损数据)(如NTFS),作为ubu ...
- Linux下Nodejs安装
网上有很多node安装方法,最好用的应该是在windows下载nodejs linux版本,安装包移植到ubuntu中, 下载下来的是个双重压缩的文件.tar.xz,先解压xz,再解压tar xz - ...