面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。

沙海 2021年6月22日05:03:46Java评论31字数 3589阅读11分57秒阅读模式
摘要

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。 程序员自修室

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。

程序员自修室 文章源自JAVA秀-https://www.javaxiu.com/34445.html

文章源自JAVA秀-https://www.javaxiu.com/34445.html

来源:github.com/c-rainstorm/blog/blob/master/tomcat/文章源自JAVA秀-https://www.javaxiu.com/34445.html

很多东西在时序图中体现的已经非常清楚了,没有必要再一步一步的作介绍,所以本文以图为主,然后对部分内容加以简单解释。文章源自JAVA秀-https://www.javaxiu.com/34445.html

绘制图形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension文章源自JAVA秀-https://www.javaxiu.com/34445.html

本文对 Tomcat 的介绍以 Tomcat-9.0.0.M22 为标准。文章源自JAVA秀-https://www.javaxiu.com/34445.html

https://tomcat.apache.org/tomcat-9.0-doc/index.html文章源自JAVA秀-https://www.javaxiu.com/34445.html

Overview

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Connector 启动以后会启动一组线程用于不同阶段的请求处理过程。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. Acceptor 线程组。用于接受新连接,并将新连接封装一下,选择一个 Poller 将新连接添加到 Poller 的事件队列中。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. Poller 线程组。用于监听 Socket 事件,当 Socket 可读或可写等等时,将 Socket 封装一下添加到 worker 线程池的任务队列中。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  3. worker 线程组。用于对请求进行处理,包括分析请求报文并创建 Request 对象,调用容器的 pipeline 进行处理。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Acceptor、Poller、worker 所在的 ThreadPoolExecutor 都维护在 NioEndpoint 中。另外,Tomcat 系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Connector Init and Start

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. initServerSocket(),通过 ServerSocketChannel.open() 打开一个 ServerSocket,默认绑定到 8080 端口,默认的连接等待队列长度是 100, 当超过 100 个时会拒绝服务。我们可以通过配置 conf/server.xml 中 Connector 的 acceptCount 属性对其进行定制。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. createExecutor() 用于创建 Worker 线程池。默认会启动 10 个 Worker 线程,Tomcat 处理请求过程中,Woker 最多不超过 200 个。我们可以通过配置 conf/server.xml 中 Connector 的 minSpareThreads 和 maxThreads 对这两个属性进行定制。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  3. Pollor 用于检测已就绪的 Socket。默认最多不超过 2 个,Math.min(2,Runtime.getRuntime().availableProcessors());。我们可以通过配置 pollerThreadCount 来定制。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  4. Acceptor 用于接受新连接。默认是 1 个。我们可以通过配置 acceptorThreadCount 对其进行定制。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Requtst Process

Acceptor

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. Acceptor 在启动后会阻塞在 ServerSocketChannel.accept(); 方法处,当有新连接到达时,该方法返回一个 SocketChannel。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. 配置完 Socket 以后将 Socket 封装到 NioChannel 中,并注册到 Poller,值的一提的是,我们一开始就启动了多个 Poller 线程,注册的时候,连接是公平的分配到每个 Poller 的。NioEndpoint 维护了一个 Poller 数组,当一个连接分配给 pollers[index] 时,下一个连接就会分配给 pollers[(index+1)%pollers.length].文章源自JAVA秀-https://www.javaxiu.com/34445.html

  3. addEvent() 方法会将 Socket 添加到该 Poller 的 PollerEvent 队列中。到此 Acceptor 的任务就完成了。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Poller

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. selector.select(1000)。当 Poller 启动后因为 selector 中并没有已注册的 Channel,所以当执行到该方法时只能阻塞。所有的 Poller 共用一个 Selector,其实现类是 sun.nio.ch.EPollSelectorImpl文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. events() 方法会将通过 addEvent() 方法添加到事件队列中的 Socket 注册到 EPollSelectorImpl,当 Socket 可读时,Poller 才对其进行处理文章源自JAVA秀-https://www.javaxiu.com/34445.html

  3. createSocketProcessor() 方法将 Socket 封装到 SocketProcessor 中,SocketProcessor 实现了 Runnable 接口。worker 线程通过调用其 run() 方法来对 Socket 进行处理。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  4. execute(SocketProcessor) 方法将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Worker

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. CoyoteAdapter 将 Rquest 提交给 Container 处理之前,并将 org.apache.coyote.Request 封装到 org.apache.catalina.connector.Request,传递给 Container 处理的 Request 是 org.apache.catalina.connector.Request。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. connector.getService().getMapper().map(),用来在 Mapper 中查询 URL 的映射关系。映射关系会保留到 org.apache.catalina.connector.Request 中,Container 处理阶段 request.getHost() 是使用的就是这个阶段查询到的映射主机,以此类推 request.getContext()、request.getWrapper() 都是。文章源自JAVA秀-https://www.javaxiu.com/34445.html

Container

面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  • 需要注意的是,基本上每一个容器的 StandardPipeline 上都会有多个已注册的 Valve,我们只关注每个容器的 Basic Valve。其他 Valve 都是在 Basic Valve 前执行。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  • request.getHost().getPipeline().getFirst().invoke() 先获取对应的 StandardHost,并执行其 pipeline。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  • request.getContext().getPipeline().getFirst().invoke() 先获取对应的 StandardContext,并执行其 pipeline。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  • request.getWrapper().getPipeline().getFirst().invoke() 先获取对应的 StandardWrapper,并执行其 pipeline。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  • 最值得说的就是 StandardWrapper 的 Basic Valve,StandardWrapperValve文章源自JAVA秀-https://www.javaxiu.com/34445.html

  1. allocate() 用来加载并初始化 Servlet,值的一提的是 Servlet 并不都是单例的,当 Servlet 实现了 SingleThreadModel 接口后,StandardWrapper 会维护一组 Servlet 实例,这是享元模式。当然了 SingleThreadModel在 Servlet 2.4 以后就弃用了。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  2. createFilterChain() 方法会从 StandardContext 中获取到所有的过滤器,然后将匹配 Request URL 的所有过滤器挑选出来添加到 filterChain 中。文章源自JAVA秀-https://www.javaxiu.com/34445.html

  3. doFilter() 执行过滤链,当所有的过滤器都执行完毕后调用 Servlet 的 service() 方法。文章源自JAVA秀-https://www.javaxiu.com/34445.html

阅读原文文章源自JAVA秀-https://www.javaxiu.com/34445.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定