面试里被问到 Netty 和 Tomcat 的差异,很多人脱口而出“一个是网络框架,一个是 Web 容器”。这句话不算错,但面试官要是继续追问“Tomcat 底层不也是 NIO 吗”“都能处理 HTTP,为什么还要 Netty”,就开始卡壳了。
真正需要讲清楚的,是两者的抽象层级和适用场景。
先纠正一个流传很广的错误
有一种说法是 Tomcat 等于 BIO,Netty 等于 NIO。这个结论早就不成立了。
Tomcat 早期确实提供过 BIO Connector,但现代 Tomcat 同样支持 NIO、NIO2 等连接器实现。所以把 Tomcat 简单归为阻塞式 I/O,是过时认知。
更准确的说法是:Netty 以异步事件驱动的网络编程模型为核心;Tomcat 也可以跑在 NIO/NIO2 之上,但它最终暴露给开发者的是 Servlet 容器模型。
两者不是新旧替代关系,而是抽象层次不同。
用餐厅和厨房打个比方
Tomcat 像一家已经装修好的餐厅。迎宾、服务员、点餐流程、厨房调度、菜品出品、客人离店,整套运营流程都替你搭好了。你只需要把自己的菜谱放进去,也就是写 Servlet 或 Controller。
Netty 更像一套高级厨房设备和编排系统。它给你灶台、传送带、调度逻辑,但菜单怎么定、流程怎么走、协议怎么设计,全由你自己决定。
所以第一层差异是:
Tomcat 是 Servlet 容器和 Web 服务器;Netty 是高性能网络应用框架。
直接说“Netty 比 Tomcat 性能高”并不严谨,因为它们解决的问题不完全重叠。
Tomcat 真正在做什么
写 Spring Boot 的时候,启动类里一行 SpringApplication.run,浏览器发一个 GET /user,请求最终落到 UserController。中间其实经过了一长串组件。
Tomcat 的核心价值之一,就是把底层网络通信和 Servlet Web 编程模型连接起来。传统 Java Web 世界里的 Servlet、Filter、Listener、Session、Cookie、HttpServletRequest、HttpServletResponse,都属于这套体系。
可以把 Tomcat 理解成一个专门服务 Java Web 应用的成熟运行底座。它不关心你卖什么菜,只负责把 HTTP 请求接进来,再按 Servlet 规范分发出去。
Netty 往下走得更低
Netty 最核心的概念不是 Servlet,而是 Channel、EventLoop、ChannelPipeline、ChannelHandler、ByteBuf、Future/Promise。
客户端发来一段 hello,Netty 让你直接面对字节流。这几个字节代表什么,由你自己定义:可以是 JSON,可以是“4 字节长度 + 消息体”,也可以是一套完全自定义的二进制协议。
这种自由度是 Netty 的立身之本。它不预设你的业务形态,只提供网络通信的抽象和调度能力。
高并发场景下 Netty 的思路
假设同时来了 10 万个连接。如果每个连接配一个线程,线程数量会非常夸张。线程不是免费的,栈空间、上下文切换、调度成本、内存、CPU,每一项都是开销。
Netty 的经典做法是少量 EventLoop 搭配大量 Channel。某个 Channel 有数据到达时,EventLoop 处理对应事件,一个 EventLoop 可以负责多个 Channel。
可以想象成一个熟练的快递分拣员,同时管理大量包裹,哪个到了就处理哪个。普通方式更像一个包裹配一个员工。
当然实际系统远比这个比喻复杂。Netty 并不是靠“线程少”就获得性能,它真正依赖的是事件驱动、非阻塞 I/O、减少线程切换,以及高度优化的内存和网络处理机制。ByteBuf 就是面向网络数据读写做过大量优化的例子。
Tomcat 其实也不弱
很多文章喜欢写“Netty 是异步非阻塞,所以比 Tomcat 快”。这个结论不能直接成立。
现代 Tomcat 本身就是成熟的高性能 Web 容器,支持 NIO Connector,有成熟的线程池、连接管理、HTTP 解析、Keep-Alive 机制。
而且普通业务系统真正慢的地方,往往根本不在网络层。数据库查询、外部接口调用、锁竞争、GC 停顿,这些才是常见的瓶颈。
这时候把 Tomcat 换成 Netty,数据库还是 100 毫秒,性能提升在哪里?
不要把网络框架性能和整个业务系统性能混为一谈。
Spring Boot 为什么默认选 Tomcat
原因很直接:Spring MVC 构建在 Servlet 模型之上。HttpServletRequest、HttpServletResponse、Filter、DispatcherServlet,本身就是 Servlet 体系的一部分。
这条链路天然顺畅。
但 Spring 还有另一套体系:Spring WebFlux。这时候就轮到 Netty 出场了。
所以经常看到 Spring MVC + Tomcat、Spring WebFlux + Netty 这样的组合。背后的核心原因不是“Tomcat 老、Netty 新”,而是两套 Web 编程模型不同。Servlet 模型和 Reactive 非阻塞模型,本身就是不同的抽象。
Netty 最厉害的地方是自由
假设开发一个 IM 系统,客户端发送用户 ID、消息类型、消息长度、消息内容。你可以自己定义协议:
怎么拆包、怎么组包、怎么编码、怎么解码、怎么处理连接、怎么做心跳、怎么做重连、怎么管理 Channel、怎么设计协议,全部由你决定。
用 Tomcat 来做这种底层 TCP 自定义协议通信,显然不是它擅长的方向。
Netty 的核心竞争力之一,就是把网络通信的控制权交给工程师。这也是 RPC 框架、IM、游戏服务器、消息推送、长连接系统里经常能看到 Netty 的原因。
Netty 能不能做 HTTP
当然能。Netty 本身就提供 HTTP 相关的编解码器和处理能力,搭建 HTTP 服务完全没问题。
但“能做”不代表“和 Tomcat 完全等价”。Tomcat 天生围绕 Servlet Web 应用打造,Netty 更像一个网络通信工具箱。用 Netty 搭 HTTP 服务,很多 Web 层能力需要自己组合。
自由越高,需要自己考虑的东西越多。
工程选型怎么判断
答案不是“Netty 性能高,所以永远选 Netty”。正确思路是看场景。
老牌 Spring MVC Web 应用,Tomcat 提供成熟的 Servlet 运行环境,开发成本低。RPC、IM、游戏、长连接、自定义 TCP 协议等场景,需要更底层、更灵活地控制网络通信,Netty 更合适。
这里有个坑:如果团队对 Netty 的内存管理和线程模型不熟悉,贸然在生产环境替换 Tomcat,可能引入难以排查的问题。Netty 的 ByteBuf 需要正确释放,否则会内存泄漏;EventLoop 上执行阻塞操作会拖垮整个线程。这些在 Tomcat 的 Servlet 模型里通常不需要开发者操心。
反过来,如果只是普通 CRUD 业务,用 Netty 自己搭一套 HTTP 服务,等于放弃了 Servlet 生态里成熟的 Filter、Session、参数绑定、异常处理,维护成本反而更高。
面试收尾怎么答
如果面试官让你总结 Netty 和 Tomcat 的差异,不要只说“一个是网络框架,一个是 Web 容器”。
可以这样组织:Tomcat 本质上是 Java Servlet 容器和 Web 服务器,主要负责 HTTP 请求处理,提供 Servlet、Filter 等 Web 应用运行环境。Netty 是异步事件驱动的网络应用框架,提供 Channel、EventLoop、Pipeline、Handler、ByteBuf 等网络编程抽象,适用于 TCP、UDP、HTTP、WebSocket 以及自定义协议等场景。
Tomcat 现代版本同样支持 NIO/NIO2,不能简单认为 Tomcat 就是 BIO、Netty 就是 NIO。两者真正的差异在于抽象层次和使用场景不同。
选型时依据应用模型、协议、连接规模、业务特征和开发维护成本来判断,而不是单纯比较谁更快。
一个可以实际验证的检查点
想确认当前 Tomcat 用的是哪种 Connector,可以查看 server.xml 里的 Connector 配置,或者启动时观察日志中 protocol 字段。默认情况下现代 Tomcat 使用 NIO。
想验证 Netty 的 EventLoop 是否被阻塞,可以在自定义 Handler 里打印当前线程名,观察是否始终落在同一个 EventLoop 线程上。如果某个 Handler 里出现了 Thread.sleep 或同步数据库调用,基本可以判定阻塞了 EventLoop。
这些检查不需要压测环境,本地就能做。
选型从来不是“谁更快”,而是“谁更适配眼前的问题”。