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.EPollSelectorImpl
events()
方法会将通过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版本:我的是企业版.(至于红帽 ...
随机推荐
- Git hook实现自动部署
Git Hook 是 Git 提供的一个钩子,能被特定的事件触发后调用.其实,更通俗的讲,当你设置了 Git Hook 后,只要你的远程仓库收到一次 push 之后,Git Hook 就能帮你执行一次 ...
- ABP框架源码学习之授权逻辑
asp.net core的默认的几种授权方法参考"雨夜朦胧"的系列博客,这里要强调的是asp.net core mvc中的授权和asp.net mvc中的授权不一样,建议先看前面& ...
- chrome浏览器下JavaScript实现clipboard时无法访问剪切板解决方案
在用JavaScript实现某个简单的复制到剪切板功能的时候,会考虑一下浏览器兼容性,主要是重点在IE和FireFox,把这个两个浏览器搞定后,基本上其他浏览器也不用太操心了,Chrome也一样,没出 ...
- html2canvas在微信中无法使用
html2canvas: https://github.com/niklasvh/html2canvas 本来想在微信网页中使用html2canvas生成图片,结果死活不行 测试发现在Chrome,手 ...
- Spring-Security自定义登录页&inMemoryAuthentication验证
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架.框架下内容比较多,可以做到按照角色权限对请求路径进行限制.今天主要验证自定义登录页,在内存用户存储中进行请求 ...
- tf.variable和tf.get_Variable以及tf.name_scope和tf.variable_scope的区别
在训练深度网络时,为了减少需要训练参数的个数(比如具有simase结构的LSTM模型).或是多机多卡并行化训练大数据大模型(比如数据并行化)等情况时,往往需要共享变量.另外一方面是当一个深度学习模型变 ...
- Jmeter简单介绍与搭配Jenkins实现自动化
Jmeter简介 Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具.用于对软件做压力测试,它最初被设计用于 Web应用测试,但后来扩展到其他测试领域. 它可以用于 ...
- yaf插件类的使用
yaf插件类的使用大小写敏感的. "插件名Plugin"为插件类的名字,这样会自动标志着这是一个插件. application.directory string 应用程序的目录,包 ...
- HashMap并发导致死循环 CurrentHashMap
为何出现死循环简要说明 HashMap闭环的详细原因 cocurrentHashMap的底层机制 为何出现死循环简要说明 HashMap是非线程安全的,在并发场景中如果不保持足够的同步,就有可能在执行 ...
- vue项目中对axios的二次封装
近来在使用vue重构公司m站时,使用了axios来进行数据的请求,由于项目的需要,对axios进行了二次封装,点击进入axios //引入axios import axios from 'axios' ...