前言
很多东西在时序图中体现的已经非常清楚了,没有必要再一步一步的作介绍,本文以图为主,然后对部分内容加以简单解释。
  • 绘制图形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension
本文对 Tomcat 的介绍以 Tomcat-9.0.0.M22 为标准。
Tomcat-9.0.0.M22 是 Tomcat 目前最新的版本,但尚未发布,它实现了 Servlet4.0 及 JSP2.3 并提供了很多新特性,需要 1.8 及以上的 JDK 支持等等,详情请查阅 Tomcat-9.0-doc
  • Overview
  • Connector Init and Start
  • Requtst Process
  • Acceptor
  • Poller
  • Worker
  • Container
  • At last
Overview
  1. Connector 启动以后会启动一组线程用于不同阶段的请求处理过程。
  2. Acceptor 线程组。用于接受新连接,并将新连接封装一下,选择一个 Poller 将新连接添加到 Poller 的事件队列中。
  3. Poller 线程组。用于监听 Socket 事件,当 Socket 可读或可写等等时,将 Socket 封装一下添加到 worker 线程池的任务队列中。
  4. worker 线程组。用于对请求进行处理,包括分析请求报文并创建 Request 对象,调用容器的 pipeline 进行处理。
  • Acceptor、 Poller、 worker 所在的 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
  1. worker 线程被创建以后就执行 ThreadPoolExecutor 的 runWorker() 方法,试图从 workQueue 中取待处理任务,但是一开始 workQueue 是空的,所以 worker 线程会阻塞在 workQueue.take() 方法。
  2. 当新任务添加到 workQueue后, workQueue.take() 方法会返回一个 Runnable,通常是 SocketProcessor, 然后 worker 线程调用 SocketProcessor 的 run() 方法对 Socket 进行处理。
  3. createProcessor() 会创建一个 Http11Processor, 它用来解析 Socket,将 Socket 中的内容封装到 Request 中。注意这个 Request 是临时使用的一个类,它的全类名是 org.apache.coyote.Request,
  4. postParseRequest() 方法封装一下 Request,并处理一下映射关系 (从 URL 映射到相应的 Host、 Context、 Wrapper)。
  5. CoyoteAdapter 将 Rquest 提交给 Container 处理之前,并将 org.apache.coyote.Request 封装到 org.apache.catalina.connector.Request,传递给 Container 处理的 Request 是 org.apache.catalina.connector.Request。
  6. connector.getService().getMapper().map(),用来在 Mapper 中查询 URL 的映射关系。映射关系会保留到 org.apache.catalina.connector.Request 中, Container 处理阶段 request.getHost() 是使用的就是这个阶段查询到的映射主机,以此类推 request.getContext()、 request.getWrapper() 都是。
  7. connector.getService().getContainer().getPipeline().getFirst().invoke() 会将请求传递到 Container 处理,当然了 Container 处理也是在 Worker 线程中执行的,但是这是一个相对独立的模块,所以单独分出来一节。
Container
  1. 需要注意的是,基本上每一个容器的 StandardPipeline 上都会有多个已注册的 Valve,我们只关注每个容器的 Basic Valve。其他 Valve 都是在 Basic Valve 前执行。
  2. request.getHost().getPipeline().getFirst().invoke() 先获取对应的 StandardHost,并执行其 pipeline。
  3. request.getContext().getPipeline().getFirst().invoke() 先获取对应的 StandardContext, 并执行其 pipeline。
  4. request.getWrapper().getPipeline().getFirst().invoke() 先获取对应的 StandardWrapper,并执行其 pipeline。
  5. 最值得说的就是 StandardWrapper 的 Basic Valve, StandardWrapperValve
  6. allocate() 用来加载并初始化 Servlet,值的一提的是 Servlet 并不都是单例的,当 Servlet 实现了 SingleThreadModel 接口后, StandardWrapper 会维护一组 Servlet 实例,这是享元模式。当然了 SingleThreadModel 在 Servlet 2.4 以后就弃用了。
  7. createFilterChain() 方法会从 StandardContext 中获取到所有的过滤器,然后将匹配 Request URL 的所有过滤器挑选出来添加到 filterChain 中。
  8. doFilter() 执行过滤链, 当所有的过滤器都执行完毕后调用 Servlet 的 service() 方法。
Reference
  1. 《How Tomcat works》
  2. 《Tomcat 架构解析》-- 刘光瑞
  3. Tomcat-9.0-doc
  4. apache-tomcat-9.0.0.M22-src
  5. tomcat 架构分析 (connector NIO 实现)
最后
欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

6张图说清楚Tomcat原理及请求流程的更多相关文章

  1. TOMCAT原理详解及请求过程(转载)

    转自https://www.cnblogs.com/hggen/p/6264475.html TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Ser ...

  2. Tomcat 原理篇

    TOMCAT 原理篇一.Tomcat 组成(Tomcat 由以下组件组成) 1.server a) Server是一个Catalina Servlet容器: b) Server 可以包含一个或多个se ...

  3. 【第十一课】Tomcat原理解析【转】

    目录 一.Tomcat顶层架构 二.Tomcat顶层架构小结: 三.Connector和Container的微妙关系 四.Connector架构分析 五.Container架构分析 六.Contain ...

  4. tomcat原理解析(一):一个简单的实现

    tomcat原理解析(一):一个简单的实现 https://blog.csdn.net/qiangcai/article/details/60583330 2017年03月07日 09:54:27 逆 ...

  5. tomcat原理分析与简单实现

    tomcat原理分析与简单实现 https://blog.csdn.net/u014795347/article/details/52328221 2016年08月26日 14:48:18 卫卫羊习习 ...

  6. 走进JavaWeb技术世界5:初探Tomcat的HTTP请求过程

    初探Tomcat的HTTP请求过程 前言:1.作为Java开发人员,大多都对Tomcat不陌生,由Apache基金会提供技术支持与维护,因为其免费开源且易用,作为Web服务器深受市场欢迎,所以有必要对 ...

  7. 《浏览器工作原理与实践》<03>HTTP请求流程:为什么很多站点第二次打开速度会很快?

    一个 TCP 连接过程包括了建立连接.传输数据和断开连接三个阶段. 而 HTTP 协议,正是建立在 TCP 连接基础之上的.HTTP 是一种允许浏览器向服务器获取资源的协议,是 Web 的基础,通常由 ...

  8. tomcat跨域请求

    tomcat A请求tomcat B中的数据(本人是在同一台机器上2个tomcat端口不同,在不同机器上有时间会测得) 博主用的是ajax请求 在A 请求B中数据,用下面的方法可以 在被请求的tomc ...

  9. Tomcat处理HTTP请求源码分析(下)

    转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...

随机推荐

  1. 在VMware通过挂载系统光盘搭建本地yum仓库

    1.首先需要有一个VMware虚拟机: 2.进去虚拟机(这里用Linux下deCentOS进行演示): 3.用root账号进行登录,否则在根目录下没有一些操作权限: 4.打开终端: 5,输入命令“cd ...

  2. 记录工作遇到的死锁问题(Lock wait timeout exceeded; try restarting transaction)

    1.问题背景 刚来新公司不久,对业务还不太熟悉,所以领导先安排我维护原有系统.大概介绍下项目背景,项目分为核心业务部分在项目A中,与第三方交互的业务在项目B中,前端发起请求调用A项目接口,并在A项目中 ...

  3. Java流程控制之(四)中断

    目录 break continue return 标签 在程序设计时,循环直接的跳转显得十分重要,虽然Java没有提供goto语句去控制程序的跳转,但为了控制循环,Java提供了continue,br ...

  4. 【Luogu P5490】扫描线

    Luogu P5490 作为一道模板题让我卡了一个月…… 对于线段树+离散化新手而言这实在是太难了…… 有关离散化: 可以查看这一篇文章:https://www.jianshu.com/p/93476 ...

  5. es3设置属性不能修改

    /*es3*/ { var Person =function () { var data ={ name:'zs', sex:'男', age:18 } this.get=function (key) ...

  6. 2019年12月4日Linux开发手记

    OK,经过昨天对V4L2工作流程的学习,现在已经大体了解了V4L2的工作原理,现在开始对V4L2的API的学习,目标:1.打开摄像头 2.储存图像 3.关闭摄像头,API网址:Linux Media ...

  7. sqlserver查询(子查询,全连接,等值连接,自然连接,左右连,交集,并集,差集)

    --部门表 create table dept( deptno int primary key,--部门编号 dname ),--部门名 loc )--地址 ); --雇员表 create table ...

  8. Debug 利器:pstack & strace

    工作中难免会遇到各种各样的 bug,对于开发环境 or 测试环境的问题还好解决,可以使用 gdb 打断点或者在代码中埋点来定位异常; 但是遇到线上的 bug 就很难受了,由于生产环境不能随意替换.中断 ...

  9. jsp html 实现隐藏输入框,点击可以取消隐藏&&弹出输入框

    jsp代码: <script language="javascript" type="text/javascript"> function chg ...

  10. .net基础概念

    .net基础概念 1.       .NET Framework 是支持生成和运行下一代应用程序和 XML Web services 的内部 Windows 组件..NET Framework 具有两 ...