一、前言
本篇已与 《Netty4 核心原理》一书无关,属于个人延伸内容
Reactor Netty 不是 Netty 官方提供的,而是由 Pivotal (现 VMware) 团队开发、基于 Netty 构建的第三方响应式网络库。
本篇内容基于下面版本内容进行:
xml
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-core</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<version>1.2.15</version>
</dependency>
二、Reactor Netty
1. 基本介绍
Reactor Netty 是基于 Netty 做的响应式封装和高层抽象**,Netty 的响应式封装,将 Netty 的异步事件模型与 Reactor 的 Publisher/Subscriber 响应式流模型深度融合。
关于 Reactor 响应式编程,详参 【笔记02】【Reactor 响应式编程】 一文。
因为 Reactor Netty 是基于 Netty 做的 Reactor 化,所以集成了二者的优点和特性:如下:
-
响应式与背压(Reactive & Backpressure)
- 完全基于 Reactive Streams 规范,所有网络操作返回
Mono/Flux,天然支持背压,避免生产者速度远超消费者导致的内存溢出。 - 连接、读写、超时、关闭等事件均以响应式信号传递,可链式组合、异常处理、重试、限流。
- 完全基于 Reactive Streams 规范,所有网络操作返回
-
全异步非阻塞(Async Non-Blocking)
- 底层复用 Netty 的 主从 Reactor 多线程模型(Boss Group + Worker Group),少量线程处理海量连接。
- 无阻塞 I/O、无阻塞线程模型,极致利用 CPU,适合高吞吐、低延迟场景。
-
协议支持全面
- HTTP/1.1、HTTP/2(客户端/服务端)
- WebSocket(全双工实时通信)
- TCP、UDP(自定义协议、长连接、消息队列)
- 内置编解码器,可快速扩展私有协议。
-
高性能与资源优化
- 继承 Netty 优势:零拷贝、池化内存(ByteBuf)、直接内存、无锁设计,降低 GC、提升吞吐。
- 连接池、复用、超时、优雅关闭等生产级特性。
-
简洁 API 与生态集成
- 提供 HttpServer、HttpClient、TcpServer、TcpClient、UdpServer、UdpClient 等开箱即用的构建器。
- 与 Spring WebFlux、Spring Cloud Gateway 深度集成,是 Spring 生态响应式 Web 的默认网络层。
需要注意的是:Reactor Netty在绝大多数场景下,会自动释放 ByteBuf,基本不需要手动调用 ReferenceCountUtil#release,这也就导致,如果我们在某些场景下不想过早释放 ByteBuf 就需要我们自己在合适的场景下调用 ReferenceCountUtil#retain 来对引用计数进行补偿。
关于 ReferenceCountUtil 的内容详参 【Netty4核心原理④】【简单实现 Tomcat 和 RPC框架功能】
2. 适用场景
| 特性 | 原生 Netty | Reactor Netty |
|---|---|---|
| 核心目标 | 极致性能、自定义协议、底层控制 | 响应式开发、快速构建微服务/网关 |
| 适合人群 | 有网络编程经验、需要定制化的开发者 | 微服务开发者、Spring 生态使用者 |
| 典型场景 | 自定义 RPC 协议、游戏服务器、中间件 | Spring Cloud 网关、WebFlux 服务、WebSocket 实时通信 |
| 开发效率 | 低(需手写大量底层配置) | 高(封装完善,开箱即用) |
| 性能 | 极致(无额外封装开销) | 接近原生(封装带来的开销可忽略) |
三、简单示例
Reactor Netty 提供「开箱即用」的构建器(HttpServer/HttpClient/TcpClient 等)。它封装了 Netty 的底层细节,比如默认配置好线程池、内存池、编解码器,新手只需关注业务逻辑,无需深入 Netty 底层。
-
原生 Netty : 基于回调(Callback)+ Future 模型,是「命令式」编程。你需要编写
ChannelHandler,通过重写channelRead()、channelActive()等方法处理网络事件,异步结果通过Future或监听器获取,代码容易出现「回调地狱」。 -
Reactor Netty : 基于Reactive Streams 规范(Mono/Flux),是「声明式」编程。所有网络操作(连接、读写、关闭)都返回响应式流对象,支持链式调用、背压、异常处理、重试等,代码更简洁、易维护。
下面我们给出两种实现方案的实例:
1. 原生 Netty 示例
java
public class NettyHttpServer {
public static void main(String[] args) throws InterruptedException {
// 1. 配置线程池
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 2. 配置服务器
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
// 3. 手动配置 HTTP 编解码器
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
// 4. 手动构建响应
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
ctx.alloc().buffer().writeBytes("Hello Netty".getBytes())
);
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
});
}
});
// 5. 绑定端口并启动
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2. Reactor Netty 示例
java
public class ReactorNettyHttpServer {
public static void main(String[] args) {
// 一行配置 + 启动,底层自动处理 Netty 线程池、编解码、内存管理
HttpServer.create()
.host("0.0.0.0")
.port(8080)
.route(routes -> routes.get("/", (req, res) -> res.sendString(Mono.just("Hello Reactor Netty"))))
.bindNow() // 绑定并启动
.onDispose() // 阻塞直到服务关闭
.block();
}
}
四、扩展内容
1. HTTP Upgrade
我们借由上面的内容,来扩展说明下 HTTP Upgrade 机制。
HTTP 协议自诞生以来历经多次重要迭代,每个版本都针对性解决了上一代的核心痛点,不断向更高效、更安全、更通用的方向演进:
-
HTTP/0.9 :作为初代协议,仅用于最基础的 HTML 文档传输,设计极简,没有请求头、状态码等复杂机制,只满足"能联网获取网页"这一最原始需求,为 Web 发展奠定了最初的通信基础。
-
HTTP/1.0 :扩展了协议能力,解决了单一资源传输的局限,正式引入请求头、响应头、状态码及更多请求方法,支持图片、脚本、样式等多种资源请求,让 Web 从纯文本走向富媒体,大幅提升了协议的可扩展性。
-
HTTP/1.1 :重点解决早期 HTTP 连接低效、性能不足的问题,通过标准化长连接、管道化请求、Host 头、缓存策略等关键机制,显著降低连接开销,提升并发与传输效率,成为长期统治互联网的主流版本。
-
HTTP/2 :针对 HTTP/1.1 队头阻塞、头部冗余、并发受限等瓶颈,采用二进制分帧、多路复用、头部压缩和服务端推送等技术,在不改变原有语义的前提下实现了性能飞跃,大幅提升现代网页加载速度。
-
HTTP/3 :突破 TCP 协议本身的限制,底层基于 QUIC 协议构建,彻底解决了 TCP 队头阻塞问题,并内置加密、支持连接迁移,在弱网、高丢包环境下依然保持极低延迟,是面向未来高体验网络的新一代标准。
而 HTTP Upgrade 机制是在 HTTP/1.1 中被正式定义和引入的核心扩展能力。根据 HTTP/1.1 标准(RFC 7230 / 旧 RFC 2616),协议新增了:Upgrade 请求头 和 101 Switching Protocols 状态码。这套机制的核心作用是:在不新建 TCP 连接的前提下,让客户端与服务器在同一条已建立的 HTTP/1.1 连接上,协商并动态切换到其他应用层协议,实现连接复用、功能扩展与协议升级。
简单概括:同一条 TCP 连接保持不变,先以 HTTP/1.1 完成协商,协商成功后,整条连接立即切换为新协议继续通信。
- 客户端在 HTTP/1.1 请求中通过
Upgrade头声明希望切换的目标协议; - 服务器若支持并同意切换,则返回
101 Switching Protocols; - 从响应完成开始,连接不再遵循 HTTP 请求-响应模型,直接使用新协议格式交互。
典型升级场景如下:
- HTTP/1.1 → WebSocket :最主流场景,将短连接 HTTP 升级为全双工长连接,用于消息推送、聊天、直播、实时通知等。
- HTTP/1.1 → h2c(明文 HTTP/2) : 在非 TLS 环境下升级到 HTTP/2,享受多路复用、头部压缩等特性。
- HTTP → TLS : 通过 Upgrade 将明文 HTTP 升级为加密通道,实现 HTTPS 能力。
- HTTP/1.1 → 其他自定义协议 : 如私有 RPC、二进制协议、物联网专用协议等,理论上可自由扩展。
特别注意的是 :HTTP/2、HTTP/3 不再支持 Upgrade,Upgrade 是 HTTP/1.1 特有的机制,HTTP/2 和 HTTP/3 已不再使用这套方案。
- HTTP/2 废除了
101 Switching Protocols,不再支持连接级协议切换; - HTTP/3 基于 QUIC 设计,从架构上就不兼容 Upgrade 模型。
因此在实际使用中:
- 在 HTTP/2 环境中建立 WebSocket → 浏览器会自动降级到 HTTP/1.1 完成 Upgrade
- 在 HTTP/3 环境中无法建立传统 WebSocket → 由新标准 WebTransport 替代
我们以 ws / wss 建立 WebSocket 的时的 HTTP Upgrade 为例说明整个流程 :
无论当前网站使用的是 HTTP/1.1、HTTP/2 还是 HTTP/3,只要客户端代码写的是标准 WebSocket,其底层建立流程永远固定如下:
WebSocket 本质是 HTTP/1.1 Upgrade 的产物。无论是 ws 还是 wss,无论服务器是否支持 HTTP/2/3,建立 WebSocket 连接时,一定、只会走 HTTP/1.1 的升级流程。
为什么 在 HTTP/1.1 场景下,原生 WebSocket 为什么不能直接建立 TCP 长连接,一定要经过 HTTP 协议升级握手?
- 端口复用与流量区分 :WebSocket 默认复用 HTTP 的 80/443 端口,和普通 HTTP 流量共用同一端口。如果客户端直接发送 WebSocket 二进制帧,服务器、反向代理无法区分这条 TCP 链路是普通 HTTP 请求还是WebSocket 长连接。必须通过一次 HTTP 升级请求做协商标记,告诉服务端:后续这条 TCP 连接不再走 HTTP 协议,切换为 WebSocket 帧协议通信。
- 复用 HTTP 现有能力,完成安全与协商 :HTTP/1.1 天然自带请求头、跨域 Origin 校验、Cookie 会话、HTTPS TLS 加密、身份认证等能力。WebSocket 直接复用这套体系,通过 Sec-WebSocket-Key、Sec-WebSocket-Accept 完成密钥校验,防连接伪造、防恶意跨域请求,同时无需重新设计一套独立的认证、加密、握手机制。
- 兼容全网中间网络设备、适配浏览器与部署规范
- 客户端解析
ws://或wss://,识别为 WebSocket 或 WebSocket + TLS; - 强制建立一条新的 HTTP/1.1 加密连接(即使页面是 HTTP/2 或 HTTP/3);
- 客户端发送 HTTP/1.1 GET 请求,携带:
Upgrade: websocketConnection: UpgradeSec-WebSocket-Key等握手信息;
- 服务器验证并同意升级,返回:
HTTP/1.1 101 Switching ProtocolsUpgrade: websocketSec-WebSocket-Accept
- 握手完成,TCP 连接不再使用 HTTP 协议;
- 后续所有数据直接使用 WebSocket 二进制帧格式进行全双工通信。
在 Reactor Netty 中,我们想建立 WebSocket 连接,底层依然严格遵循 HTTP/1.1 的 Upgrade 协议升级流程,并不会因为服务器支持 HTTP/2 或 HTTP/3 而改变 WebSocket 的建立规则。
在 Reactor Netty 中我们通过 sendWebSocket() 来完成 HTTP Upgrade,如下:
java
// 服务端代码
public class WebSocketServerUpgradeDemo {
public static void main(String[] args) {
DisposableServer server = HttpServer.create()
.port(8080)
// 强制使用 HTTP/1.1,保证 Upgrade 机制可用
.protocol(HttpProtocol.HTTP11)
.route(routes -> routes
.get("/ws", (req, res) -> {
// ==============================
// 这里 sendWebSocket 就是升级动作
// 内部执行 HTTP Upgrade 流程
// ==============================
return res.sendWebSocket((in, out) -> {
// 升级完成后,客户端和服务端的通信通过 in 和 out 完成
// 订阅客户端发送的 WebSocket 帧
return in.receive()
.cast(WebSocketFrame.class)
.doOnNext(frame -> {
// 处理客户端消息
System.out.println("服务端收到:" + frame.content().toString());
})
// 2. out:向客户端发送 WebSocket 消息
.then(out.sendString(Mono.just("协议升级成功,我是服务端")));
});
})
)
.bindNow();
System.out.println("WebSocket 服务已启动,等待客户端升级协议...");
server.onDispose().block();
}
}
// 客户端发起 Upgrade 握手
public class WebSocketClientUpgradeDemo {
public static void main(String[] args) {
WebSocketClient.create()
.host("localhost")
.port(8080)
.uri("/ws")
.handle((in, out) -> {
// 升级完成后,客户端和服务端的通信通过 in 和 out 完成
// 客户端发送消息
out.sendString(Mono.just("我来自客户端,协议已升级为 WebSocket"))
.subscribe();
// 接收服务端消息
return in.receive()
.doOnNext(frame -> {
System.out.println("客户端收到:" + frame.getPayload());
})
.then();
})
.connect()
.block();
System.out.println("WebSocket 协议升级完成,连接已建立");
}
}
流程概述如下:
-
客户端连接服务端 → 建立 TCP 连接
-
客户端发送 HTTP/1.1 请求,包含:
txtplaintext Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xxx -
服务端进入
/ws路由 → 执行sendWebSocket() -
sendWebSocket ()内部识别为协议升级 → 返回 101 状态码 -
客户端收到 101 → 升级完成
-
连接从 HTTP/1.1 切换为 WebSocket 长连接,开始双向通信
in 和 out 是 WebSocket 长连接的通信管道,升级完成后,服务端和客户端的通信则 完全通过 in 和 out 进行双向传输。
2. route 和 handle
在 Reactor Netty 中,HttpServer#route() 与 HttpServer#handle() 最核心的区别:route 是带路由匹配的高级 API(支持路径、方法、参数),handle 是全局兜底 / 原始处理函数(所有请求都走它)
-
handle(handler):全局唯一处理器-
所有 HTTP 请求(无论路径、方法)都进入这一个
(req, res) → Publisher<Void> -
没有内置路由、没有路径匹配、没有方法过滤
-
示例代码如下:
javaHttpServer.create() .port(8080) // 所有请求都走这里:GET /a、POST /b、404 都进同一个 handler .handle((req, res) -> { String path = req.path(); if ("/hello".equals(path) && "GET".equals(req.method().name())) { return res.sendString(Mono.just("Hello")); } // 所有不匹配的都返回 404 res.status(404); return res.sendString(Mono.just("Not Found")); }) .bindNow();
-
-
route(routes -> { ... }):路由表构建器-
内部用 HttpServerRoutes 注册多条规则:
get("/a")、post("/b")、ws("/c") -
请求按声明顺序匹配第一个符合条件的路由
-
支持路径变量
/users/{id}、文件路由、WebSocket 路由 -
示例代码如下:
javaHttpServer.create() .port(8080) .route(routes -> routes // GET /hello .get("/hello", (req, res) -> res.sendString(Mono.just("Hello"))) // POST /echo .post("/echo", (req, res) -> res.send(req.receive())) // 路径参数 /path/123 → param=123 .get("/path/{param}", (req, res) -> res.sendString(Mono.just("param=" + req.param("param")))) // WebSocket .ws("/ws", (in, out) -> out.send(in.receive())) // 静态文件 .file("/static/**", "public/") ) .bindNow();
-
五、参考内容
- 豆包