【Tomcat】Tomcat 运行原理

Tomcat 运行原理

  • 一. Servlet 运行原理
    • 1. 接收请求
    • 2. 根据请求计算响应
    • 3. 返回响应
  • 二. Tomcat 的伪代码
    • 1. Tomcat 初始化流程
    • 2. Tomcat 处理请求流程
    • 3. Servlet 的 service 方法的实现

一. Servlet 运行原理

在 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代码是如何被调用的呢? 响应又是如何返回给浏览器的?

Tomcat 的定位

在这里插入图片描述

当浏览器给服务器发送请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求.
HTTP 协议作为一个应用层协议, 需要底层协议栈来支持工作. 如下图所示:

在这里插入图片描述

更详细的交互过程如下:

在这里插入图片描述

1. 接收请求

  • 用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求.
  • 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 通过物理层的硬件设备转换成光信号/电信号传输出去.
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机.
  • 服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成 HTTP 请求. 并交给 Tomcat 进程进行处理 (根据端口号确定进程)
  • Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请求的方法 (GET/POST/…), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息.

注意: 每一个请求就对应一个 Servlet 实例

2. 根据请求计算响应

  • 在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码. 我们自己的代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等.

3. 返回响应

  • 我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去.
  • 此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去.
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机.
  • 浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成 HTTP 响应, 并交给浏览器处理.
  • 浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把 body 中的数据按照一定的格式显示在浏览器的界面上.

二. Tomcat 的伪代码

1. Tomcat 初始化流程

class Tomcat {// 用来存储所有的 Servlet 对象private List<Servlet> instanceList = new ArrayList<>();public void start() {// 根据约定,读取 WEB-INF/web.xml 配置文件;// 并解析被 @WebServlet 注解修饰的类// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.Class<Servlet>[] allServletClasses = ...;// 这里要做的的是实例化出所有的 Servlet 对象出来;for (Class<Servlet> cls : allServletClasses) {// 这里是利用 java 中的反射特性做的// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。Servlet ins = cls.newInstance();instanceList.add(ins);}// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;for (Servlet ins : instanceList) {ins.init();}// 利用我们之前学过的知识,启动一个 HTTP 服务器// 并用线程池的方式分别处理每一个 RequestServerSocket serverSocket = new ServerSocket(8080);// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况ExecuteService pool = Executors.newFixedThreadPool(100);while (true) {Socket socket = ServerSocket.accept();// 这里以短连接为例// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的pool.execute(new Runnable() {doHttpRequest(socket);});}// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;for (Servlet ins : instanceList) {ins.destroy();}}public static void main(String[] args) {new Tomcat().start();}
}

小结:

  • Tomcat 的代码中内置了 main 方法. 当我们启动 Tomcat 的时候, 就是从 Tomcat 的 main 方法开始执行的.
  • 被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取到, 并集中管理.
  • Tomcat 通过 反射 这样的语法机制来创建被 @WebServlet 注解修饰的类的实例.
  • 这些实例被创建完了之后, 会点调用其中的 init 方法进行初始化.
    (这个方法是 HttpServlet 自带的, 默认啥都不干, 我们可以在继承 HttpServlet 时重写 init, 从而做一些初始化的工作)
  • 这些实例被销毁之前, 会调用其中的 destory 方法进行收尾工作.
    (这个方法也是 HttpServlet 自带的, 默认什么都不做, 我们自己写的类可以重写 destory)
  • Tomcat 内部也是通过 Socket API 进行网络通信.
  • Tomcat 为了能同时响应多个 HTTP 请求, 采取了多线程的方式实现, 因此 Servlet 是运行在 多线程环境 下的.
    (这里使用的是短连接, 即每个请求处理完毕都断开连接, 下次请求重新建立连接).

2. Tomcat 处理请求流程

class Tomcat {void doHttpRequest(Socket socket) {// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和构建响应HttpServletRequest req = HttpServletRequest.parse(socket);HttpServletRequest resp = HttpServletRequest.build(socket);// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态内容// 直接使用我们学习过的 IO 进行内容输出if (file.exists()) {// 返回静态内容return;}// 走到这里的逻辑都是动态内容了// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条// 最终找到要处理本次请求的 Servlet 对象Servlet ins = findInstance(req.getURL());// 调用 Servlet 对象的 service 方法// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了try {ins.service(req, resp);} catch (Exception e) {// 返回 500 页面,表示服务器内部错误}}
}

小结:

  • Tomcat 从 Socket 中读到的 HTTP 请求是一个字符串, 然后会按照 HTTP 协议的格式解析成一个 HttpServletRequest 对象.
  • Tomcat 会根据 URL 中的 path 判定这个请求是请求一个静态资源还是动态资源. 如果是静态资源, 直接找到对应的文件把文件的内容通过 Socket 返回. 如果是动态资源, 才会执行到 Servlet 的相关逻辑.
  • Tomcat 会根据 URL 中的 Context Path 和 Servlet Path 确定要调用哪个 Servlet 实例的 service 方法.
  • 通过 service 方法, 就会进一步调用到我们之前写的 doGet 或者 doPost

3. Servlet 的 service 方法的实现

class Servlet {public void service(HttpServletRequest req, HttpServletResponse resp) {String method = req.getMethod();if (method.equals("GET")) {doGet(req, resp);} else if (method.equals("POST")) {doPost(req, resp);} else if (method.equals("PUT")) {doPut(req, resp);} else if (method.equals("DELETE")) {doDelete(req, resp);} ......}
}

小结:

  • Servlet 的 service 方法内部会根据当前请求的方法, 决定调用其中的某个 doXXX 方法.
  • 在调用 doXXX 方法的时候, 就会触发 多态 机制, 从而执行到我们自己写的子类中的 doXXX 方法.

我们前面自己写的 HelloServlet 类, 继承自 HttpServlet 类. 而 HttpServlet 又继承自 Servlet. 相当于 HelloServlet 就是 Servlet 的子类.
接下来, 在 Tomcat 启动阶段, Tomcat 已经根据注解的描述, 创建了 HelloServlet 的实例, 然后把这个实例放到了 Servlet 数组中.
后面我们根据请求的 URL 从数组中获取到了该 HelloServlet 实例, 但是我们是通过 Servlet ins这样的父类引用来获取到 HelloServlet 实例的.
最后, 我们通过 ins.doGet() 这样的代码调用 doGet 的时候, 正是 “父类引用指向子类对象”, 此时就会触发多态机制, 从而调用到我们之前在 HelloServlet 中所实现的 doGet 方法

等价代码:

Servlet ins = new HelloServlet();
ins.doGet(req, resp);

在这里插入图片描述
注意: HttpServlet 的实例只是在程序启动时创建一次. 而不是每次收到 HTTP 请求都重新创建实例.

好啦! 以上就是对 Tomcat 运行原理 的讲解,希望能帮到你 !
评论区欢迎指正 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140257.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM Linux DIY(十三)Qt5 移植

前言 板子带有屏幕&#xff0c;那当然要设计一下 GUI&#xff0c;对 Qt5 比较熟悉&#xff0c;那就移植它吧。 移植 Qt5 buildroot 使能 Qt5&#xff0c;这里我们只开启核心功能 gui module --> widgets module 编译 $ make ODIY_V3S/ qt5base编译报错&#xff1a;找不…

Flink TaskManger 内存计算实战

Flink TaskManager内存计算图 计算实例 案例一、假设Task Process内存4GB。 taskmanager.memory.process.size4096m 先排减JVM内存。 JVM Metaspace 固定内存 256mJVM Overhead 固定比例 process * 0.1 4096 * 0.1 410m 得到 Total Flink Memory 4096-256-410 3430m 计…

【线性代数】为什么 AA* = |A|E

A A ∗ 矩阵相乘&#xff0c;刚好是行列式展开的定义 AA*矩阵相乘&#xff0c;刚好是行列式展开的定义 AA∗矩阵相乘&#xff0c;刚好是行列式展开的定义 矩阵提取一个因子 ∣ A ∣ &#xff0c;所有元素需要除 ∣ A ∣ 矩阵提取一个因子 |A|&#xff0c;所有元素需要除 |A| 矩…

【C/C++笔试练习】——printf在使用%的注意事项、for循环语句的三个条件、运算符优先级、删除公共字符

文章目录 C/C笔试练习1.%符号在printf用作格式说明符的注意事项&#xff08;1&#xff09;输出%5.3s&#xff08;2&#xff09;判断%中小数点含义 2.for循环语句的三个条件&#xff08;3&#xff09;判断循环次数&#xff08;4&#xff09;判断循环次数 3.运算符优先级&#xf…

【ACDC数据集】:预处理ACDC心脏3D MRI影像数据集到VOC数据集格式,nii转为jpg,label转为png

【Segment Anything Model】做分割的专栏链接&#xff0c;欢迎来学习。 【博主微信】cvxiaoyixiao 本专栏为公开数据集的预处理&#xff0c;持续更新中。 文章目录 1️⃣ ACDC数据集介绍2️⃣ ACDC数据集样例 3️⃣ 预处理ACDC目标 4️⃣ 处理结果样图 5️⃣ 代码 6️⃣ 划分测…

文件高效批量重命名,轻松重命名不同类型的文件名并隐藏编号

你是否曾经因为文件名混乱而感到困扰&#xff1f;你是否希望有一种方法可以快速、简单地管理你的文件名&#xff1f;如果你的答案是肯定的&#xff0c;那么我们的产品——文件重命名工具&#xff0c;将是你的完美解决方案&#xff01; 首先我们要进入文件批量改名高手主页面&a…

Sqilte3初步教程

文章目录 安装创建数据库创建和删除表插入行数据 安装 Windows下安装&#xff0c;首先到下载页面&#xff0c;下载Windows安装软件&#xff0c;一般是 sqlite-dll-win32-*.zip sqlite-tools-win32-*.zip下载之后将其内容解压到同一个文件夹下&#xff0c;我把它们都放在了D:\…

搭建ELK+Filebead+zookeeper+kafka实验

部署 Zookeeper 集群 准备 3 台服务器做 Zookeeper 集群 192.168.10.17 192.168.10.21 192.168.10.22 1.安装前准备 关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装 JDK yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-…

DolphinDB x 龙蜥社区,打造多样化的数据底座

近日&#xff0c;浙江智臾科技有限公司&#xff08;以下简称“DolphinDB”&#xff09;正式签署 CLA 贡献者许可协议&#xff0c;加入龙蜥社区&#xff08;OpenAnolis&#xff09;。 DolphinDB 主创团队从 2012 年开始投入研发产品。作为一款基于高性能时序数据库&#xff0c;D…

【pytest】 allure 生成报告

1. 下载地址 官方文档; Allure Framework 参考文档&#xff1a; 最全的PytestAllure使用教程&#xff0c;建议收藏 - 知乎 https://github.com/allure-framework 1.2安装Python依赖 windows&#xff1a;pip install allure-pytest 2. 脚本 用例 import pytest class …

代码随想录算法训练营 动态规划part12

一、最佳买卖股票时机含冷冻期 309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; public class Solution {public int maxProfit(int[] prices) {int len prices.length;if (len < 2) {return 0;}int[] dp new int[3];dp[0] 0;dp[1] -price…

leetcode:2446. 判断两个事件是否存在冲突(python3解法)

难度&#xff1a;简单 给你两个字符串数组 event1 和 event2 &#xff0c;表示发生在同一天的两个闭区间时间段事件&#xff0c;其中&#xff1a; event1 [startTime1, endTime1] 且event2 [startTime2, endTime2] 事件的时间为有效的 24 小时制且按 HH:MM 格式给出。 当两个…

terraform简单的开始-vpc cvm创建

从网络开始 从创建VPC开始 复用前面的main.tf的代码&#xff1a; terraform {required_providers {tencentcloud {source "tencentcloudstack/tencentcloud"version "1.81.25"}} } variable "region" {description "腾讯云地域"…

Spring Boot的新篇章:探索2.0版的创新功能

文章目录 引言1. Spring Boot 2.0的响应式编程2. 自动配置的改进3. Spring Boot 2.0的嵌入式Web服务器4. Spring Boot 2.0的Actuator端点5. Spring Boot 2.0的Spring Data改进6. Spring Boot 2.0的安全性增强7. Spring Boot 2.0的监控和追踪8. Spring Boot 2.0的测试改进结论 &…

Learn Prompt-提供示例

目前我们与 ChatGPT 交流的主要形式是文字。提示除了指令问题的形式外&#xff0c;还可以包含例子。特别是当我们需要具体的输出时&#xff0c;提供例子可以省去我们对具体任务的解释&#xff0c;帮助ChatGPT更好地理解我们的确切需求&#xff0c;从而提供更准确&#xff0c;更…

http协议与tomcat

目录 引言 抓包 fiddler的基本使用及设置 HTTP请求 请求首行请求头空行正文 请求的首行方法URL版本号 ​编辑 响应首行响应头空行正文 响应的首行版本号状态码 URL(网址) url基本格式 urlencode 常见方法 get和post区别 认识请求"报头"(header) Host Content-Len…

数据结构-----堆(完全二叉树)

目录 前言 一.堆 1.堆的概念 2.堆的存储方式 二.堆的操作方法 1.堆的结构体表示 2.数字交换接口函数 3.向上调整&#xff08;难点&#xff09; 4.向下调整&#xff08;难点&#xff09; 5.创建堆 6.堆的插入 7.判断空 8.堆的删除 9.获取堆的根(顶)元素 10.堆的遍历…

提升群辉AudioStation音乐体验,实现公网音乐播放

文章目录 本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是本教程使用环境&#xff1a;1 群晖系统安装audiostation套件2 下载移动端app3 内网穿透&#xff0c;映射至公网 很多老铁想在上班路上听点喜欢的歌或者相声解解闷儿&#xff0c;于是打开手…

什么是HTTP头部(HTTP headers)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 理解 HTTP 头部&#xff08;HTTP Headers&#xff09;⭐ HTTP 头部的分类⭐ HTTP 头部的应用⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#x…

C++ 学习系列 -- std::vector (未完待续)

一 std::vector 是什么&#xff1f; vector 是c 中一种序列式容器&#xff0c;与前面说的 array 类似&#xff0c;其内存分配是连续的&#xff0c;但是与 array 不同的地方在于&#xff0c;vector 在运行时是可以动态扩容的&#xff0c;此外 vector 提供了许多方便的操作&…