【Netty4核心原理⑱】【Reactor Netty】

一、前言

本篇已与 《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 化,所以集成了二者的优点和特性:如下:

  1. 响应式与背压(Reactive & Backpressure)

    • 完全基于 Reactive Streams 规范,所有网络操作返回 Mono/Flux,天然支持背压,避免生产者速度远超消费者导致的内存溢出。
    • 连接、读写、超时、关闭等事件均以响应式信号传递,可链式组合、异常处理、重试、限流。
  2. 全异步非阻塞(Async Non-Blocking)

    • 底层复用 Netty 的 主从 Reactor 多线程模型(Boss Group + Worker Group),少量线程处理海量连接。
    • 无阻塞 I/O、无阻塞线程模型,极致利用 CPU,适合高吞吐、低延迟场景。
  3. 协议支持全面

    • HTTP/1.1、HTTP/2(客户端/服务端)
    • WebSocket(全双工实时通信)
    • TCP、UDP(自定义协议、长连接、消息队列)
    • 内置编解码器,可快速扩展私有协议。
  4. 高性能与资源优化

    • 继承 Netty 优势:零拷贝、池化内存(ByteBuf)、直接内存、无锁设计,降低 GC、提升吞吐。
    • 连接池、复用、超时、优雅关闭等生产级特性。
  5. 简洁 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 完成协商,协商成功后,整条连接立即切换为新协议继续通信。

  1. 客户端在 HTTP/1.1 请求中通过 Upgrade 头声明希望切换的目标协议;
  2. 服务器若支持并同意切换,则返回 101 Switching Protocols
  3. 从响应完成开始,连接不再遵循 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 完成密钥校验,防连接伪造、防恶意跨域请求,同时无需重新设计一套独立的认证、加密、握手机制。
  • 兼容全网中间网络设备、适配浏览器与部署规范
  1. 客户端解析 ws://wss://,识别为 WebSocket 或 WebSocket + TLS;
  2. 强制建立一条新的 HTTP/1.1 加密连接(即使页面是 HTTP/2 或 HTTP/3);
  3. 客户端发送 HTTP/1.1 GET 请求,携带:
    • Upgrade: websocket
    • Connection: Upgrade
    • Sec-WebSocket-Key 等握手信息;
  4. 服务器验证并同意升级,返回:
    • HTTP/1.1 101 Switching Protocols
    • Upgrade: websocket
    • Sec-WebSocket-Accept
  5. 握手完成,TCP 连接不再使用 HTTP 协议
  6. 后续所有数据直接使用 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 协议升级完成,连接已建立");
    }
}

流程概述如下:

  1. 客户端连接服务端 → 建立 TCP 连接

  2. 客户端发送 HTTP/1.1 请求,包含:

    txt 复制代码
    plaintext
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: xxx
  3. 服务端进入 /ws 路由 → 执行 sendWebSocket()

  4. sendWebSocket () 内部识别为协议升级 → 返回 101 状态码

  5. 客户端收到 101 → 升级完成

  6. 连接从 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>

    • 没有内置路由、没有路径匹配、没有方法过滤

    • 示例代码如下:

      java 复制代码
      HttpServer.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 路由

    • 示例代码如下:

      java 复制代码
      HttpServer.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();

五、参考内容

  1. 豆包
相关推荐
Code_Artist7 天前
一天之内我让 AI 用 Netty 造了一个最小可用的 MVC 框架:体验一下造轮子的快感😅!
后端·netty·ai编程
斯普润布特9 天前
物联网-Spring+Netty 框架整合
java·物联网·netty
笨手笨脚の10 天前
Netty 如何高效的使用内存
netty·堆外内存
佛祖让我来巡山13 天前
Netty保姆级全解析|技术背景+核心知识点+生产实战教程
netty
佛祖让我来巡山15 天前
Netty入门|从BIO到Netty:一步步看懂Java网络编程的迭代逻辑
netty·nio·bio
文慧的科技江湖22 天前
光伏储能充电系统PRD功能列表 - 慧知开源充电桩平台
开发语言·开源·netty·慧知开源充电桩平台·开源充电桩平台
不早睡不改名@1 个月前
Netty源码分析---Reactor线程模型深度解析(一)
java·笔记·学习·netty
zs宝来了1 个月前
Netty Reactor 模型:Boss、Worker 与 EventLoop
reactor·netty·源码解析·线程模型·eventloop
不早睡不改名@1 个月前
Netty源码分析---Reactor线程模型深度解析(二)
java·网络·笔记·学习·netty