Tomcat 如何处理HTTP请求

Tomcat处理HTTP请求的完整流程,本质上可以拆解为三步:

  1. 连接器(Connector)接收并解析请求

  2. 容器(Container)根据路由找到具体Servlet执行

  3. 封装响应并返回给客户端


第一步:连接器(Connector)接收请求

Tomcat启动时,会开启一个Acceptor线程,它在指定端口(默认8080)上监听新的TCP连接。

  1. 建立连接:当浏览器发起HTTP请求,Acceptor接受Socket连接。

  2. 读取数据 :将Socket交给**线程池(Executor)**处理,从Socket的InputStream中读取原始的HTTP报文数据(字节流)。

  3. 解析报文 :Coyote适配器 将字节流解析成Request和Response对象。注意,这里的Request是Tomcat内部定义的org.apache.coyote.Request,它包含:

    • 请求行(方法、URI、协议版本)

    • 请求头(Host、User-Agent等)

    • 请求体(如果是POST/PUT等)

  4. 封装传递 :Tomcat把内部的Request封装成标准的HttpServletRequest,交给Catalina容器(Engine)处理。


第二步:容器(Container)路由与执行

Tomcat的容器是层层嵌套 的,结构为:Engine > Host > Context > Wrapper。

路由与执行的过程叫 "Pipeline-Value"管道机制,每一层都有阀门(Valve):

容器层级 职责
Engine 整个Tomcat引擎,根据请求的域名(Host头)匹配对应的虚拟主机。
Host 代表一个虚拟主机(如localhost),根据Context路径匹配Web应用。
Context 代表一个Web应用(如/myapp),根据URL路径匹配具体的Servlet。
Wrapper 代表一个Servlet实例,负责加载并调用Servlet的service()方法。

具体流程:

  1. 请求到达Engine ,Engine的Valve会根据Host头找到对应的Host容器。

  2. Host 的Valve会根据URL的应用上下文(如/shop)找到对应的Context容器。

  3. Context 的Valve会根据URL的路径(如/user/login)去Mapper组件 中查找匹配的Wrapper。

    • Mapper维护了所有Servlet的映射关系(web.xml或@WebServlet注解)。
  4. 找到匹配的Wrapper后,Wrapper的Valve会做三件关键事:

    • 加载Servlet :如果Servlet未实例化,通过类加载器加载并调用init()(仅第一次)。

    • 获取线程 :从线程池中取出一个线程(注意,Tomcat默认是单例多线程 模型,一个Servlet只有一个实例,但多个线程同时调用service())。

    • 调用Servlet :调用service(req, res)方法,根据HTTP方法(GET/POST)派发到doGet()或doPost()。


第三步:处理业务逻辑与响应

  1. 你的业务代码(写在doGet/doPost里)执行,比如查询数据库、处理JSON数据。

  2. 通过HttpServletResponse对象将结果(HTML、JSON等)写入输出流。

  3. Tomcat负责收尾:

    • 将Response对象中的内容,按照HTTP协议格式组装成响应报文(状态行、响应头、响应体)。

    • 通过Socket的输出流将字节数据写回给客户端浏览器。

    • 请求处理完成后,归还线程给线程池,等待下一个请求。


两个容易被误解的细节(加分项)

  • 关于线程模型 :Tomcat 8/9默认使用 NIO(非阻塞I/O) ,一个请求不独占一个线程直到业务结束。但在service()方法执行期间,线程确实是绑定的,直到响应返回才释放。

  • 关于Request对象 :Tomcat内部为了性能,HttpServletRequest中的InputStream是基于Socket流 的。如果你在业务代码里调用request.getInputStream(),它实际上直接读取的是底层Socket的数据,而不是预先缓存好的副本(除非你手动读取)。

相关推荐
文丑颜不良啊1 分钟前
LeetCode76~100题目解析
java·leetcode
她的男孩5 分钟前
页面已发布用户仍进不去:Forge Admin 门户入口与系统菜单挂载解耦
java·spring boot·低代码
不像程序员的程序媛8 分钟前
mybatisplus insert dto未指定id时会如何
java·开发语言
我命由我1234527 分钟前
5-2.Python 编码进阶 - 字符串进阶(拼接、普通格式化、普通格式化数字精度控制、快速格式化、遍历、常用方法)
java·开发语言·后端·python·学习·java-ee·学习方法
Pniubi8 小时前
HashMap的扩容和树化边界
java·开发语言
Mikko79 小时前
用 Java 复刻了高中时在塞班上玩的「熔岩矿车」:纯 Swing、零依赖、没有一张图片,一个 jar 双击就能玩
java·游戏开发·swing
知了学历孙老师9 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
Java爱好狂.9 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
秋饼10 小时前
LLM API密钥动态轮换实战:Spring Vault + Spring AI + 多Provider路由
java·ai·技术分享·后端开发
福兮说11 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json