我们日常用Spring Boot写的RestController,感觉上就是一个简单的方法,但它背后其实有一套复杂的网络服务在支撑。一个HTTP请求到底是怎么从用户的浏览器,穿过层层网络,最终抵达我们代码里的Controller方法的?理解这个过程,特别是Tomcat和Netty这两种主流服务器的处理方式,对我们写出更高性能的应用很有帮助。

Tomcat模型:一个请求,一个线程,简单直接

我们先聊聊大家最熟悉的Spring MVC on Tomcat组合。

Tomcat的NIO模型,本质上是一种高度优化的“单Reactor多线程”实现。它的内部角色分工很明确:有一小队Acceptor线程,专门负责在门口迎接新的TCP连接。连接一旦建立,Acceptor不会亲自服务,而是把这个连接交给Poller线程。

Poller线程也只有少数几个,它像个雷达,不断扫描所有已连接的通道,看看哪个通道上送来了数据。一旦发现某个连接有数据可读,Poller也不会自己去读写,而是把这个“可读”的信号打包成一个任务,扔给一个庞大的Worker线程池。

接下来就是重头戏了。Worker线程池里的一个工作线程会领走这个任务,并且负责到底。这个线程会先去网络通道里把请求数据读出来,这个读取的过程是阻塞的。读完后,它把字节流解析成我们熟悉的HttpServletRequest对象,然后请求就进入了Spring的处理流程,最终调用到我们的Controller方法。

这里最关键的一点是,我们写的业务代码,包括查询数据库、调用其他服务等所有操作,全都在这个Worker线程上同步执行。执行完了,再由这个线程把响应数据写回给用户。所以,这种模式的好处是编程模型非常简单,写代码就像写单机程序一样,思路很直观。但缺点也很明显,系统的并发能力,直接受限于Worker线程池的大小。

Netty模型:事件驱动,少数线程支撑海量连接

再来看看Spring WebFlux on Netty这个组合,它的玩法就完全不一样了。

Netty是标准的“主从Reactor多线程”架构。它也有一个专门负责接客的BossGroup,通常就一个线程,工作很专一,只管接收连接,然后把连接转手扔给WorkerGroup

WorkerGroup里包含了一组EventLoop线程,数量通常和CPU核心数差不多。一个连接被分配给某个EventLoop之后,这个连接的整个生命周期就和这个线程绑定了。从数据读取、解码成HttpRequest对象,再到分发给WebFlux框架,所有事情都由这一个EventLoop线程亲力亲为。

当请求最终到达我们的Controller方法时,代码依然是运行在这个EventLoop线程上的。这就带来一个严格的约束:绝对不能有任何阻塞操作。因为一旦这个线程被阻塞,它负责的所有其他连接就全都动不了了。

所以,我们必须返回MonoFlux这类响应式类型,并通过异步方式去调用下游。EventLoop线程在发起数据库查询或者RPC调用后,会立即返回去处理其他连接上的事件,而不是原地等待。当下游服务返回结果时,会通过一个回调事件,重新唤醒这个EventLoop线程,让它继续完成后续的数据处理和响应。这种事件驱动的模式,使得极少数的线程就能管理海量的并发连接。

核心差异与如何选择

为了更直观地看出差别,我们看一个表格。

特性维度Spring MVC on TomcatSpring WebFlux on Netty
线程模型一个请求一个Worker线程少数I/O线程处理所有连接
编程范式同步、阻塞异步、非阻塞 (响应式)
资源占用线程多,内存开销大线程少,内存开销小
核心风险线程池耗尽阻塞I/O线程
适用场景通用CRUD、CPU密集型业务高并发、I/O密集型业务(如网关)

这两种模型的性能差异,在处理I/O密集的场景时会被无限放大。比如,处理1000个并发请求,每个请求都要等待1秒的网络I/O。在Tomcat里,如果Worker线程池大小是200,那200个线程会立刻被占满并阻塞,剩下的800个请求只能排队。而在Netty里,哪怕只有8个EventLoop线程,也能轻松应对,因为它们从不等待。

那么,我们到底该怎么选?

其实很简单,看你的业务场景。如果你的应用是I/O密集型的,比如微服务网关、消息推送中台,需要用有限的服务器资源应对海量的并发连接,那么Netty/WebFlux是更好的选择,它能最大化系统吞吐能力。

反过来,如果你的应用是业务逻辑复杂、并发量可控的传统CRUD系统,那Tomcat/MVC的同步模型会让开发、调试和排查问题变得简单直观得多。在这种场景下,开发效率和可维护性的重要性,往往比压榨那一点硬件性能更重要。

总的来说,Tomcat用线程池隔离了阻塞,让编程更简单;Netty用事件驱动压榨硬件性能,让并发更高。两者没有绝对的优劣,理解它们背后的设计思想,才能在合适的场景做出正确的选择。

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

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

相关文章

GO学习记录十——发包

记录下不同平台的发包操作和期间遇到的问题 1.命令: $env:GOOSlinux $env:GOARCHamd64 go build -o release/HTTPServices-linux第一行,配置平台,linux、windows 第二行,配置部署服务器的处理器架构 第三行,输出目标文…

贪心算法与动态规划

1. 什么是贪心算法? 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。 核心思想:“每步都贪心地选择眼前最好的,不去考虑整个未来的长…

学会“读网页”:生成式 AI 在足球赛事信息整理中的实战

逐步教程(Step-by-Step) — 适合初学者与教学类文章 背景(为什么要这样做) 对于足球迷、资讯编辑与数据分析师来说,最快、最准确把握一场比赛的核心信息至关重要:比分、关键事件(进球、点球、红…

BM3D 图像降噪快速算法的 MATLAB 实现

BM3D 图像降噪快速算法的 MATLAB 实现1. 快速 BM3D 算法流程(概述)步骤操作加速技巧① 分组块匹配 堆叠FFT 互相关② 协同滤波3D 变换 硬阈值FFT 沿第三维③ 聚合加权平均稀疏矩阵累加 2. 核心函数(单文件版) 保存为 bm3d_fast.…

Go的schedt调度(runtime/proc.go)

1. 创建go的入口函数// Create a new g running fn. // Put it on the queue of gs waiting to run. // The compiler turns a go statement into a call to this. func newproc(fn *funcval) {gp : getg()pc : sys.GetCallerPC()systemstack(func() {newg : newproc1(fn, gp, …

Ubuntu 服务器配置转发网络访问

配置文档:Ubuntu 服务器转发网络访问 一、网络拓扑以以下网络拓扑为示例Ubuntu 服务器(两个网卡) eth1 10.66.71.222 (接入内网)eno1 192.168.2.100 (直连相机) 相机ip 192.168.2.1 Windows 客…

为什么企业需要高防IP

1. 抵御日益猖獗的DDoS攻击 现代DDoS攻击规模已突破Tbps级别 传统防火墙无法应对大规模流量攻击 高防IP采用分布式清洗中心,可轻松抵御300Gbps以上的攻击流量 2. 保障业务连续性 网络中断1小时可能造成数百万损失 高防IP确保服务99.99%可用性 智能切换机制实…

CSS基础 - 选择器备忘录 --笔记5

目录基础选择器组合器伪类选择器属性选择器选择器可以选中页面上的特定元素并为其指定样式。 CSS有多种选择器。 基础选择器 标签选择器 – tagname:匹配目标元素的标签名。优先级是0,0,1。如:p、h1、div类选择器 – .class:匹配class属性中…

自动驾驶中的传感器技术46——Radar(7)

卫星雷达(又称为分布式雷达)主要讲当前雷达的雷达信号处理计算以及雷达目标相关的一些感知算法都迁移到中央域控进行,雷达端基本只负责数据采集,这样做的影响如下: 雷达端成本与功耗降低; 雷达端采样得到的…

【论文阅读】Diff-Privacy: Diffusion-based Face Privacy Protection

基于扩散模型的人脸隐私保护方法——DiffPrivacy,解决了两类人脸隐私任务:匿名化(anonymization)和视觉身份信息隐藏(visual identity information hiding)。1. 研究背景随着人工智能和大数据技术的普及&am…

React 原理篇 - 深入理解虚拟 DOM

一、什么是虚拟 DOM? 在前端开发中,“虚拟 DOM” 是一个高频出现的术语,尤其在 React 生态中被广泛讨论。但很多开发者对它的理解往往停留在 “JS 对象” 这个表层认知上。 实际上,虚拟 DOM 是一种编程概念—— 在这个概念里&…

对汇编的初理解

此处是一个简单的函数,里面将调用了一个函数add()函数这里是函数的原型这里是调用lcd函数产生的汇编语言,翻译过来就是r11,r0cnt(r4cnt,前文有提及),然后调用add函数,此处BL是指会回到指令的下一…

《Python 自动化实战:从零构建一个文件同步工具》

《Python 自动化实战:从零构建一个文件同步工具》 一、开篇引入:为什么我们需要文件同步? 你是否有过这样的困扰: 公司电脑和家里电脑上都有工作项目,每次更新都要手动复制? U 盘频繁传输文件,不仅麻烦还容易出错? 项目文件夹动辄几 G,每次同步都耗时长、效率低? 在…

工业相机与镜头的靶面尺寸详解:选型避坑指南

在机器视觉系统中,相机与镜头的靶面尺寸匹配是一个非常关键却又经常被忽略的细节。选错了,不但影响图像质量,还可能导致画面“黑角”、视野不符、镜头浪费等问题。 今天我们就用通俗易懂的方式,聊一聊相机与镜头靶面尺寸的那些事儿…

使用 Go 和 go-commons 实现内存指标采集并对接 Prometheus

文章目录一、准备工作二、编写内存采集代码三、运行 Exporter四、接入 Prometheus五、可扩展思路总结在运维和监控领域,资源指标采集 是必不可少的一环。CPU、内存、磁盘、网络这些系统资源,需要实时采集并上报到监控系统中。 本文以 内存指标采集 为例&…

webrtc弱网-IntervalBudget类源码分析与算法原理

一、核心功能 IntervalBudget 类用于基于时间窗口的带宽预算管理。它根据设定的目标比特率(kbps)和一个固定时间窗口(500ms),计算在该时间窗口内可用的字节数(即“预算”),并支持预…

深度学习基本模块:RNN 循环神经网络

循环神经网络(RNN)是一种专门用于处理序列数据的神经网络架构。与处理空间数据的卷积神经网络(Conv2D)不同,RNN通过引入循环连接使网络具有"记忆"能力,能够利用之前的信息来影响当前的输出&#…

React18学习笔记(二) React的状态管理工具--Redux,案例--移动端外卖平台

文章目录一.Redux的基础用法1.示例:普通网页中的Redux计步器2.Redux管理数据的流程3.配套工具和环境准备3.1.配套工具3.2.环境准备4.示例:React项目中的Redux计步器思路步骤step1:创建子模块step2:导入子模块step3:注入store实例step4:React组件内使用store中的数据step5:在组件…

34.Socket编程(UDP)(上)

点分十进制字符串IP 转 32位网络序列IP 分析:1)IP转成4字节 2)4字节转成网络序列 思路: "192.168.1.1" 进行字符串划分,以 "." 为分割符,分割出"192",&qu…

Redis的持久化工具包—RDB AOF

文章目录 前言 一、RDB 持久化(快照持久化) 1. 定义 2. RDB 触发机制 (1)手动触发 (2)自动触发 3. RDB 持久化流程 4. RDB 核心配置 5. RDB 优缺点 二、AOF 持久化(日志持久化) 1. 定…