Tomcat:网络请求原理分析
一、Http 请求过程总览
浏览器请求
http://localhost/test/index.jsp
用户点击网页内容,请求被发送到本机端口 8080,被在那里监听的 Coyote HTTP/1.1 Connector 获得。
Connector 将 Request 包装成 ServletRequest 给它所在的 Service 的 Engine 来处理,并等待 Engine 的回应。
Engine 获得请求 localhost/test/index.jsp,匹配所有的虚拟主机 Host。
Engine 匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机),名为 localhost 的 Host 获得请求/test/index.jsp,匹配它所拥有的所有的 Context。Host 匹配到路径为/test 的 Context(如果匹配不到就把该请求交给路径名为""的 Context 去处理)。
path="/test"的 Context 获得请求/index.jsp,在它的 mapping table 中寻找出对应的 Servlet。Context 匹配到 URL PATTERN 为*.jsp 的 Servlet,对应于 JspServlet 类。
构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet 的 doGet()或 doPost().执行业务逻辑、数据存储等程序。
Context 把执行完之后的 HttpServletResponse 对象返回给 Host。
Host 把 HttpServletResponse 对象返回给 Engine。
Engine 把 HttpServletResponse 对象返回 Connector,Connector 把 ServletResponse 对象封装成 Response。
Connector 把 Response 返回给客户 Browser。
二、具体组件及源码分析
2.1 外部网络请求
2.1.1 Connector
用户在浏览器中输入一个 URL 地址之后浏览器将发起一个 Http 的请求,通过网络传输数据,请求到我们的 Tomcat 服务器中,Tomcat 使用 Connector 接收 Socket 请求数据并通过 Coyote 链接器封装底层网络通信,为 Catalina 容器提供了统一的接口。
在 Coyote 中,Tomcat 支持一下 3 种协议:
HTTP/1.1 协议: 目前最常用的访问协议
AJP 协议: Apache 提供的一种协议,用于和 Apache HTTP Server 集成。
HTTP/2.0 协议: 下一代 HTTP 协议,自 Tomcat8.5 版本开始支持。
针对 HTTP 和 AJP 协议,Coyote 又按照 I/O 方式分别提供了不同的方案。
BIO: 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成,Tomcat 早期网络请求的默认实现方式(自 8.5 版本开始,已经移除)
NIO: 同步非阻塞的 I/O 模型(当前 Tomcat 的默认实现)
NIO2: 异步非阻塞 I/O 模型
APR: Apache 可移植运行库。
2.1.2 NioEndpoint
我们以 NioEndpoint 为例,来看一下 Tomcat 是如何实现网络请求收发的。
我们发现 Tomcat 默认使用 HTTP/1.1 协议创建 Http11NioProtocol,它默认使用 NioEndpoint
当 Connector 执行 init()的同时运行了 protocolHandler.init()最终运行了 NioEndpoint.init(),最终就是启动了一个 ServerSocket 并绑定监听端口。
当 Connector 执行 start()方法时,最终执行了:NioEndpoint.startInternal()方法。该方法内部启动了一个 Poller 线程,一个 Acceptor 线程。其中,Acceptor 用于监听客户端连接,并将 Socket 连接放入待处理事件缓存池。Poller 循环从待处理的事件缓存队列中拿到请求交给线程池处理。Poller 将 Socket 请求包装成为:SocketProcessor 对象,执行 processor.process()方法,最终调用:getAdapter().service(request, response);这里的 getAdapter 即 CoyoteAdapter,最终找到容器中管道方法的第一个阀门方法,发起调用,此时,网络请求正式进入我们容器中。
TIPS 如果想要详细了解 Tomcat 接收请求的流程图可以查看官网提供的资料
http://tomcat.apache.org/tomcat-9.0-doc/architecture/requestProcess/request-process.png
2.2 容器内部请求路径匹配
在 Tomcat 启动的时候 Service 持有了一个 Mapper 对象,它借助 MapperListener 在应用启动的过程中,将 Engine,Host,Context,Wrapper 路径都搜集了起来。当一个请求过来的时候它会去当前 Service 中去匹配,最终将请求执行到我们 StandardWrapperValve。此时会创建 ApplicationFilterChain 过滤器链;通过调用过滤器内部方法 internalDoFilter;最终调用:javax.servlet.http.HttpServlet.service()方法,请求到应用中!
三、本文小结
我们通过一个 Http 请求路径分析了 Tomcat 是使用 Socket 接收网络请求,使用 Coyote 转换我们的网络协议参数,包装为 ServletRequest 和 ServletResponse,使用匹配请求路径的方式最终找到我们应用中的 Servlet 地址。至此,可以说我们对 Tomcat 的主要功能基本上已经了解的七七八八了,后续我们将会陆续的分享一些工作中常用的小技巧。
程序员的核心竞争力其实还是技术,因此对技术还是要不断的学习,关注 “IT 巅峰技术” 公众号 ,该公众号内容定位:中高级开发、架构师、中层管理人员等中高端岗位服务的,除了技术交流外还有很多架构思想和实战案例。
作者是 《 消息中间件 RocketMQ 技术内幕》 一书作者,同时也是 “RocketMQ 上海社区”联合创始人,曾就职于拼多多、德邦等公司,现任上市快递公司架构负责人,主要负责开发框架的搭建、中间件相关技术的二次开发和运维管理、混合云及基础服务平台的建设。
版权声明: 本文为 InfoQ 作者【IT巅峰技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/5413dca5732a7f459ac0d9d2f】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论