08|Reactor Netty 与 Spring Cloud Gateway:WebFlux 如何站在 Netty 之上

08|Reactor Netty 与 Spring Cloud Gateway:WebFlux 如何站在 Netty 之上

前面几篇我们已经从 Netty 自身看过:

ServerBootstrap 启动流程

EventLoop 线程模型

Pipeline 责任链

ByteBuf 内存管理

writeAndFlush 写出与背压

半包粘包与编解码

这一篇把视角往上抬一层:

  • Spring WebFlux
  • Reactor Netty
  • Spring Cloud Gateway

很多人知道:

  • Spring Cloud Gateway 底层是 WebFlux。
  • WebFlux 底层常见是 Reactor Netty。
  • Reactor Netty 底层是 Netty。

但这句话如果不拆开,很容易变成口号。

这篇文章要回答几个问题:

WebFlux 一定等于 Netty 吗?

Reactor Netty 到底封装了 Netty 的什么?

Spring Cloud Gateway 为什么默认依赖 Reactor Netty?

Gateway 转发请求时,Netty 在底层做了什么?

为什么不能在 Gateway Filter 里写阻塞代码?

如果放到云边协同系统里,Gateway 不是一个"统一入口"的名词装饰,而是一类非常具体的工程边界。

云端 HTTP 网关负责接入 Web、App、开放 API 或内部服务调用;MQTT Gateway 负责设备消息、云边指令、协议适配和反回环;媒体网关或流媒体服务负责 RTSP、RTMP、WebRTC、HLS、GB28181 等不同媒体协议之间的接入、转封装和播放链路。它们处理的协议不同,但共同点都是:自己不一定生产最终业务结果,却必须稳定地编排大量 IO

所以理解 Spring Cloud Gateway,不能只停在"它能路由请求"。更关键的是要理解:

  • 请求从客户端进来后,在哪个线程上被读取;
  • Filter 链什么时候执行,能不能阻塞;
  • 请求体和响应体是否被缓存、复制或流式转发;
  • 下游慢、客户端慢、连接池满时压力堆在哪里;
  • 网关到底承担路由、鉴权、限流、协议转换,还是业务聚合。

这些问题背后,仍然是前面几篇讲过的 EventLoop、Pipeline、ByteBuf、背压和编解码。

先给结论:

WebFlux 是响应式 Web 编程模型;

Reactor 是响应式流框架;

Reactor Netty 是基于 Netty 的响应式网络运行时;

Spring Cloud Gateway Server WebFlux 是站在 WebFlux + Reactor Netty 之上的网关实现。

先说明本文范围:

  • 本文讨论的是 Spring Cloud Gateway Server WebFlux 这条经典链路。
  • Spring Cloud Gateway 生态中也存在 Web MVC 相关形态,但那不是本文重点。

一、WebFlux 不等于 Netty

先纠正一个常见误解:

WebFlux = Netty

这个说法不准确。

更准确的是:

WebFlux 是 Spring 的响应式 Web 框架。

它可以运行在不同底层服务器上。

Spring 官方文档明确提到,WebFlux 支持:

Tomcat

Jetty

Servlet containers

Netty

Undertow

也就是说:

  • WebFlux 本身是上层编程模型,
  • Netty 只是它可以使用的一种运行时。

不过在 Spring Boot WebFlux 场景里,默认运行时通常是:

Reactor Netty

原因也很自然:

Netty 更适合异步、非阻塞、事件驱动网络模型。

所以可以这样理解:

  • WebFlux 不一定必须用 Netty;
  • 但 WebFlux 最典型、最自然的运行时是 Reactor Netty。

二、Spring Cloud Gateway 和普通 WebFlux 不一样

普通 WebFlux 应用可以切换底层运行时。

但 Spring Cloud Gateway Server WebFlux 更特殊。

官方文档明确说明:

  • Spring Cloud Gateway is built on Spring Boot, Spring WebFlux, and Project Reactor.
  • Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux.
  • It does not work in a traditional Servlet Container or when built as a WAR.

翻译成工程语言就是:

经典 Spring Cloud Gateway Server WebFlux 依赖 Reactor Netty;

它不是传统 Spring MVC + Tomcat Servlet 模型;

也不能像传统 WAR 那样部署到外部 Servlet 容器。

所以如果你看到的是 Gateway MVC 相关能力,要把它和本文讨论的 Gateway Server WebFlux 区分开。

所以 Gateway 的典型链路是:
#mermaid-svg-KzL2BXYIiVgBw9l1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KzL2BXYIiVgBw9l1 .error-icon{fill:#552222;}#mermaid-svg-KzL2BXYIiVgBw9l1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KzL2BXYIiVgBw9l1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .marker.cross{stroke:#333333;}#mermaid-svg-KzL2BXYIiVgBw9l1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KzL2BXYIiVgBw9l1 p{margin:0;}#mermaid-svg-KzL2BXYIiVgBw9l1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster-label text{fill:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster-label span{color:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster-label span p{background-color:transparent;}#mermaid-svg-KzL2BXYIiVgBw9l1 .label text,#mermaid-svg-KzL2BXYIiVgBw9l1 span{fill:#333;color:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .node rect,#mermaid-svg-KzL2BXYIiVgBw9l1 .node circle,#mermaid-svg-KzL2BXYIiVgBw9l1 .node ellipse,#mermaid-svg-KzL2BXYIiVgBw9l1 .node polygon,#mermaid-svg-KzL2BXYIiVgBw9l1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .rough-node .label text,#mermaid-svg-KzL2BXYIiVgBw9l1 .node .label text,#mermaid-svg-KzL2BXYIiVgBw9l1 .image-shape .label,#mermaid-svg-KzL2BXYIiVgBw9l1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-KzL2BXYIiVgBw9l1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .rough-node .label,#mermaid-svg-KzL2BXYIiVgBw9l1 .node .label,#mermaid-svg-KzL2BXYIiVgBw9l1 .image-shape .label,#mermaid-svg-KzL2BXYIiVgBw9l1 .icon-shape .label{text-align:center;}#mermaid-svg-KzL2BXYIiVgBw9l1 .node.clickable{cursor:pointer;}#mermaid-svg-KzL2BXYIiVgBw9l1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .arrowheadPath{fill:#333333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KzL2BXYIiVgBw9l1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KzL2BXYIiVgBw9l1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KzL2BXYIiVgBw9l1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster text{fill:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 .cluster span{color:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KzL2BXYIiVgBw9l1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KzL2BXYIiVgBw9l1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-KzL2BXYIiVgBw9l1 .icon-shape,#mermaid-svg-KzL2BXYIiVgBw9l1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KzL2BXYIiVgBw9l1 .icon-shape p,#mermaid-svg-KzL2BXYIiVgBw9l1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KzL2BXYIiVgBw9l1 .icon-shape .label rect,#mermaid-svg-KzL2BXYIiVgBw9l1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KzL2BXYIiVgBw9l1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KzL2BXYIiVgBw9l1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KzL2BXYIiVgBw9l1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Spring Cloud Gateway
Spring WebFlux
Project Reactor
Reactor Netty
Netty
NIO / epoll

这和普通 Spring MVC 链路不同:
#mermaid-svg-ocO4hjl9SylEmEdq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ocO4hjl9SylEmEdq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ocO4hjl9SylEmEdq .error-icon{fill:#552222;}#mermaid-svg-ocO4hjl9SylEmEdq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ocO4hjl9SylEmEdq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ocO4hjl9SylEmEdq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ocO4hjl9SylEmEdq .marker.cross{stroke:#333333;}#mermaid-svg-ocO4hjl9SylEmEdq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ocO4hjl9SylEmEdq p{margin:0;}#mermaid-svg-ocO4hjl9SylEmEdq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ocO4hjl9SylEmEdq .cluster-label text{fill:#333;}#mermaid-svg-ocO4hjl9SylEmEdq .cluster-label span{color:#333;}#mermaid-svg-ocO4hjl9SylEmEdq .cluster-label span p{background-color:transparent;}#mermaid-svg-ocO4hjl9SylEmEdq .label text,#mermaid-svg-ocO4hjl9SylEmEdq span{fill:#333;color:#333;}#mermaid-svg-ocO4hjl9SylEmEdq .node rect,#mermaid-svg-ocO4hjl9SylEmEdq .node circle,#mermaid-svg-ocO4hjl9SylEmEdq .node ellipse,#mermaid-svg-ocO4hjl9SylEmEdq .node polygon,#mermaid-svg-ocO4hjl9SylEmEdq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ocO4hjl9SylEmEdq .rough-node .label text,#mermaid-svg-ocO4hjl9SylEmEdq .node .label text,#mermaid-svg-ocO4hjl9SylEmEdq .image-shape .label,#mermaid-svg-ocO4hjl9SylEmEdq .icon-shape .label{text-anchor:middle;}#mermaid-svg-ocO4hjl9SylEmEdq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ocO4hjl9SylEmEdq .rough-node .label,#mermaid-svg-ocO4hjl9SylEmEdq .node .label,#mermaid-svg-ocO4hjl9SylEmEdq .image-shape .label,#mermaid-svg-ocO4hjl9SylEmEdq .icon-shape .label{text-align:center;}#mermaid-svg-ocO4hjl9SylEmEdq .node.clickable{cursor:pointer;}#mermaid-svg-ocO4hjl9SylEmEdq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ocO4hjl9SylEmEdq .arrowheadPath{fill:#333333;}#mermaid-svg-ocO4hjl9SylEmEdq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ocO4hjl9SylEmEdq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ocO4hjl9SylEmEdq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ocO4hjl9SylEmEdq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ocO4hjl9SylEmEdq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ocO4hjl9SylEmEdq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ocO4hjl9SylEmEdq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ocO4hjl9SylEmEdq .cluster text{fill:#333;}#mermaid-svg-ocO4hjl9SylEmEdq .cluster span{color:#333;}#mermaid-svg-ocO4hjl9SylEmEdq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ocO4hjl9SylEmEdq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ocO4hjl9SylEmEdq rect.text{fill:none;stroke-width:0;}#mermaid-svg-ocO4hjl9SylEmEdq .icon-shape,#mermaid-svg-ocO4hjl9SylEmEdq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ocO4hjl9SylEmEdq .icon-shape p,#mermaid-svg-ocO4hjl9SylEmEdq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ocO4hjl9SylEmEdq .icon-shape .label rect,#mermaid-svg-ocO4hjl9SylEmEdq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ocO4hjl9SylEmEdq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ocO4hjl9SylEmEdq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ocO4hjl9SylEmEdq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Spring MVC
Servlet
Tomcat worker thread
阻塞式请求处理模型

Gateway 的定位决定了它更适合事件驱动模型。

因为它本质上是:

  • 网关
  • 路由器
  • 反向代理
  • IO 编排器

而不是普通 CRUD 业务服务。

三、Reactor Netty 是什么?

Reactor Netty 可以理解成:

把 Netty 包装成 Reactor 风格的响应式网络库。

Netty 原生模型是:

Channel

EventLoop

Pipeline

ChannelHandler

ByteBuf

ChannelFuture

Reactor 的模型是:

Mono

Flux

Publisher

Subscriber

backpressure

Reactor Netty 做的事情就是把二者连接起来。

对于 HTTP 服务端,它提供:

HttpServer

对于 HTTP 客户端,它提供:

HttpClient

Reactor Netty 官方文档里也说,HttpServer 隐藏了创建 HTTP Server 所需的大部分 Netty 功能,并加入 Reactive Streams 背压。

所以它不是替代 Netty。

它是:

站在 Netty 上面,暴露响应式 API。

四、从 Netty 到 Reactor Netty 的模型转换

Netty 的底层读事件大致是:
#mermaid-svg-ko84Jkl3wLWTCGeO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ko84Jkl3wLWTCGeO .error-icon{fill:#552222;}#mermaid-svg-ko84Jkl3wLWTCGeO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ko84Jkl3wLWTCGeO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ko84Jkl3wLWTCGeO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ko84Jkl3wLWTCGeO .marker.cross{stroke:#333333;}#mermaid-svg-ko84Jkl3wLWTCGeO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ko84Jkl3wLWTCGeO p{margin:0;}#mermaid-svg-ko84Jkl3wLWTCGeO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster-label text{fill:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster-label span{color:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster-label span p{background-color:transparent;}#mermaid-svg-ko84Jkl3wLWTCGeO .label text,#mermaid-svg-ko84Jkl3wLWTCGeO span{fill:#333;color:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO .node rect,#mermaid-svg-ko84Jkl3wLWTCGeO .node circle,#mermaid-svg-ko84Jkl3wLWTCGeO .node ellipse,#mermaid-svg-ko84Jkl3wLWTCGeO .node polygon,#mermaid-svg-ko84Jkl3wLWTCGeO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ko84Jkl3wLWTCGeO .rough-node .label text,#mermaid-svg-ko84Jkl3wLWTCGeO .node .label text,#mermaid-svg-ko84Jkl3wLWTCGeO .image-shape .label,#mermaid-svg-ko84Jkl3wLWTCGeO .icon-shape .label{text-anchor:middle;}#mermaid-svg-ko84Jkl3wLWTCGeO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ko84Jkl3wLWTCGeO .rough-node .label,#mermaid-svg-ko84Jkl3wLWTCGeO .node .label,#mermaid-svg-ko84Jkl3wLWTCGeO .image-shape .label,#mermaid-svg-ko84Jkl3wLWTCGeO .icon-shape .label{text-align:center;}#mermaid-svg-ko84Jkl3wLWTCGeO .node.clickable{cursor:pointer;}#mermaid-svg-ko84Jkl3wLWTCGeO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ko84Jkl3wLWTCGeO .arrowheadPath{fill:#333333;}#mermaid-svg-ko84Jkl3wLWTCGeO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ko84Jkl3wLWTCGeO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ko84Jkl3wLWTCGeO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ko84Jkl3wLWTCGeO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ko84Jkl3wLWTCGeO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ko84Jkl3wLWTCGeO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster text{fill:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO .cluster span{color:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ko84Jkl3wLWTCGeO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ko84Jkl3wLWTCGeO rect.text{fill:none;stroke-width:0;}#mermaid-svg-ko84Jkl3wLWTCGeO .icon-shape,#mermaid-svg-ko84Jkl3wLWTCGeO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ko84Jkl3wLWTCGeO .icon-shape p,#mermaid-svg-ko84Jkl3wLWTCGeO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ko84Jkl3wLWTCGeO .icon-shape .label rect,#mermaid-svg-ko84Jkl3wLWTCGeO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ko84Jkl3wLWTCGeO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ko84Jkl3wLWTCGeO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ko84Jkl3wLWTCGeO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} socket readable
EventLoop
ChannelPipeline
ChannelInboundHandler.channelRead(ByteBuf)

Reactor Netty 要把这个模型转换成:

Flux<DataBuffer / ByteBuf>

也就是:
#mermaid-svg-lU8KEbvVPzthsZ1w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lU8KEbvVPzthsZ1w .error-icon{fill:#552222;}#mermaid-svg-lU8KEbvVPzthsZ1w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lU8KEbvVPzthsZ1w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lU8KEbvVPzthsZ1w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lU8KEbvVPzthsZ1w .marker.cross{stroke:#333333;}#mermaid-svg-lU8KEbvVPzthsZ1w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lU8KEbvVPzthsZ1w p{margin:0;}#mermaid-svg-lU8KEbvVPzthsZ1w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster-label text{fill:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster-label span{color:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster-label span p{background-color:transparent;}#mermaid-svg-lU8KEbvVPzthsZ1w .label text,#mermaid-svg-lU8KEbvVPzthsZ1w span{fill:#333;color:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w .node rect,#mermaid-svg-lU8KEbvVPzthsZ1w .node circle,#mermaid-svg-lU8KEbvVPzthsZ1w .node ellipse,#mermaid-svg-lU8KEbvVPzthsZ1w .node polygon,#mermaid-svg-lU8KEbvVPzthsZ1w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lU8KEbvVPzthsZ1w .rough-node .label text,#mermaid-svg-lU8KEbvVPzthsZ1w .node .label text,#mermaid-svg-lU8KEbvVPzthsZ1w .image-shape .label,#mermaid-svg-lU8KEbvVPzthsZ1w .icon-shape .label{text-anchor:middle;}#mermaid-svg-lU8KEbvVPzthsZ1w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lU8KEbvVPzthsZ1w .rough-node .label,#mermaid-svg-lU8KEbvVPzthsZ1w .node .label,#mermaid-svg-lU8KEbvVPzthsZ1w .image-shape .label,#mermaid-svg-lU8KEbvVPzthsZ1w .icon-shape .label{text-align:center;}#mermaid-svg-lU8KEbvVPzthsZ1w .node.clickable{cursor:pointer;}#mermaid-svg-lU8KEbvVPzthsZ1w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lU8KEbvVPzthsZ1w .arrowheadPath{fill:#333333;}#mermaid-svg-lU8KEbvVPzthsZ1w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lU8KEbvVPzthsZ1w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lU8KEbvVPzthsZ1w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lU8KEbvVPzthsZ1w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lU8KEbvVPzthsZ1w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lU8KEbvVPzthsZ1w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster text{fill:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w .cluster span{color:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lU8KEbvVPzthsZ1w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lU8KEbvVPzthsZ1w rect.text{fill:none;stroke-width:0;}#mermaid-svg-lU8KEbvVPzthsZ1w .icon-shape,#mermaid-svg-lU8KEbvVPzthsZ1w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lU8KEbvVPzthsZ1w .icon-shape p,#mermaid-svg-lU8KEbvVPzthsZ1w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lU8KEbvVPzthsZ1w .icon-shape .label rect,#mermaid-svg-lU8KEbvVPzthsZ1w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lU8KEbvVPzthsZ1w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lU8KEbvVPzthsZ1w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lU8KEbvVPzthsZ1w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 底层不断到来的网络数据
响应式数据流

HTTP 请求体可以被表示成:

Flux

HTTP 响应写出可以表示成:

Publisher

这样上层 WebFlux 就不需要直接操作 Netty Handler。

它面对的是:

  • Mono
  • Flux
  • ServerWebExchange

而底层真正收发数据的仍然是:

  • Netty Channel
  • EventLoop
  • Pipeline
  • ByteBuf

五、WebFlux 请求链路大概怎么走?

一个 WebFlux 请求进来,大致链路是:
#mermaid-svg-W0Jus6qfRngtd2QX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W0Jus6qfRngtd2QX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W0Jus6qfRngtd2QX .error-icon{fill:#552222;}#mermaid-svg-W0Jus6qfRngtd2QX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W0Jus6qfRngtd2QX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W0Jus6qfRngtd2QX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W0Jus6qfRngtd2QX .marker.cross{stroke:#333333;}#mermaid-svg-W0Jus6qfRngtd2QX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W0Jus6qfRngtd2QX p{margin:0;}#mermaid-svg-W0Jus6qfRngtd2QX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W0Jus6qfRngtd2QX .cluster-label text{fill:#333;}#mermaid-svg-W0Jus6qfRngtd2QX .cluster-label span{color:#333;}#mermaid-svg-W0Jus6qfRngtd2QX .cluster-label span p{background-color:transparent;}#mermaid-svg-W0Jus6qfRngtd2QX .label text,#mermaid-svg-W0Jus6qfRngtd2QX span{fill:#333;color:#333;}#mermaid-svg-W0Jus6qfRngtd2QX .node rect,#mermaid-svg-W0Jus6qfRngtd2QX .node circle,#mermaid-svg-W0Jus6qfRngtd2QX .node ellipse,#mermaid-svg-W0Jus6qfRngtd2QX .node polygon,#mermaid-svg-W0Jus6qfRngtd2QX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W0Jus6qfRngtd2QX .rough-node .label text,#mermaid-svg-W0Jus6qfRngtd2QX .node .label text,#mermaid-svg-W0Jus6qfRngtd2QX .image-shape .label,#mermaid-svg-W0Jus6qfRngtd2QX .icon-shape .label{text-anchor:middle;}#mermaid-svg-W0Jus6qfRngtd2QX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W0Jus6qfRngtd2QX .rough-node .label,#mermaid-svg-W0Jus6qfRngtd2QX .node .label,#mermaid-svg-W0Jus6qfRngtd2QX .image-shape .label,#mermaid-svg-W0Jus6qfRngtd2QX .icon-shape .label{text-align:center;}#mermaid-svg-W0Jus6qfRngtd2QX .node.clickable{cursor:pointer;}#mermaid-svg-W0Jus6qfRngtd2QX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W0Jus6qfRngtd2QX .arrowheadPath{fill:#333333;}#mermaid-svg-W0Jus6qfRngtd2QX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W0Jus6qfRngtd2QX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W0Jus6qfRngtd2QX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W0Jus6qfRngtd2QX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W0Jus6qfRngtd2QX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W0Jus6qfRngtd2QX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W0Jus6qfRngtd2QX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W0Jus6qfRngtd2QX .cluster text{fill:#333;}#mermaid-svg-W0Jus6qfRngtd2QX .cluster span{color:#333;}#mermaid-svg-W0Jus6qfRngtd2QX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W0Jus6qfRngtd2QX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W0Jus6qfRngtd2QX rect.text{fill:none;stroke-width:0;}#mermaid-svg-W0Jus6qfRngtd2QX .icon-shape,#mermaid-svg-W0Jus6qfRngtd2QX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W0Jus6qfRngtd2QX .icon-shape p,#mermaid-svg-W0Jus6qfRngtd2QX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W0Jus6qfRngtd2QX .icon-shape .label rect,#mermaid-svg-W0Jus6qfRngtd2QX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W0Jus6qfRngtd2QX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W0Jus6qfRngtd2QX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W0Jus6qfRngtd2QX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端连接
Netty Channel
Netty EventLoop
Reactor Netty HttpServer
HttpHandler
WebHandler
WebFilter 链
HandlerMapping
HandlerAdapter
Controller / HandlerFunction
Mono / Flux 响应
Reactor Netty 写回
Netty Channel writeAndFlush

所以 WebFlux 表面上写的是:

java 复制代码
return Mono.just("hello");

但底层仍然要经过:

Netty 连接

事件循环

响应编码

写队列

socket 写出

这就是为什么理解 Netty 后,再看 WebFlux 会清楚很多。

WebFlux 不是绕开了网络编程。

它是把网络编程包装成响应式模型。

六、Spring Cloud Gateway 的核心链路

Spring Cloud Gateway 的核心不是 Controller。

它的核心是:

Route

Predicate

Filter

NettyRoutingFilter

NettyWriteResponseFilter

请求进来后,大致流程是:
#mermaid-svg-c6twxbmjtA7Wb9jF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c6twxbmjtA7Wb9jF .error-icon{fill:#552222;}#mermaid-svg-c6twxbmjtA7Wb9jF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c6twxbmjtA7Wb9jF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c6twxbmjtA7Wb9jF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c6twxbmjtA7Wb9jF .marker.cross{stroke:#333333;}#mermaid-svg-c6twxbmjtA7Wb9jF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c6twxbmjtA7Wb9jF p{margin:0;}#mermaid-svg-c6twxbmjtA7Wb9jF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster-label text{fill:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster-label span{color:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster-label span p{background-color:transparent;}#mermaid-svg-c6twxbmjtA7Wb9jF .label text,#mermaid-svg-c6twxbmjtA7Wb9jF span{fill:#333;color:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF .node rect,#mermaid-svg-c6twxbmjtA7Wb9jF .node circle,#mermaid-svg-c6twxbmjtA7Wb9jF .node ellipse,#mermaid-svg-c6twxbmjtA7Wb9jF .node polygon,#mermaid-svg-c6twxbmjtA7Wb9jF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-c6twxbmjtA7Wb9jF .rough-node .label text,#mermaid-svg-c6twxbmjtA7Wb9jF .node .label text,#mermaid-svg-c6twxbmjtA7Wb9jF .image-shape .label,#mermaid-svg-c6twxbmjtA7Wb9jF .icon-shape .label{text-anchor:middle;}#mermaid-svg-c6twxbmjtA7Wb9jF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-c6twxbmjtA7Wb9jF .rough-node .label,#mermaid-svg-c6twxbmjtA7Wb9jF .node .label,#mermaid-svg-c6twxbmjtA7Wb9jF .image-shape .label,#mermaid-svg-c6twxbmjtA7Wb9jF .icon-shape .label{text-align:center;}#mermaid-svg-c6twxbmjtA7Wb9jF .node.clickable{cursor:pointer;}#mermaid-svg-c6twxbmjtA7Wb9jF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-c6twxbmjtA7Wb9jF .arrowheadPath{fill:#333333;}#mermaid-svg-c6twxbmjtA7Wb9jF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-c6twxbmjtA7Wb9jF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-c6twxbmjtA7Wb9jF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c6twxbmjtA7Wb9jF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-c6twxbmjtA7Wb9jF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c6twxbmjtA7Wb9jF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster text{fill:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF .cluster span{color:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-c6twxbmjtA7Wb9jF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-c6twxbmjtA7Wb9jF rect.text{fill:none;stroke-width:0;}#mermaid-svg-c6twxbmjtA7Wb9jF .icon-shape,#mermaid-svg-c6twxbmjtA7Wb9jF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c6twxbmjtA7Wb9jF .icon-shape p,#mermaid-svg-c6twxbmjtA7Wb9jF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-c6twxbmjtA7Wb9jF .icon-shape .label rect,#mermaid-svg-c6twxbmjtA7Wb9jF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c6twxbmjtA7Wb9jF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-c6twxbmjtA7Wb9jF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-c6twxbmjtA7Wb9jF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端请求
Gateway HandlerMapping
匹配 Route
执行 GatewayFilter 链
NettyRoutingFilter 转发到下游
下游响应返回
NettyWriteResponseFilter 写回客户端

Gateway 本质上在协调两段连接:

客户端 -> Gateway

Gateway -> 下游服务

它需要同时处理:

客户端请求体读取

下游连接建立

请求转发

下游响应读取

响应写回客户端

超时

限流

重试

熔断

连接池

这正是事件驱动和非阻塞 IO 擅长的场景。

七、NettyRoutingFilter 做什么?

NettyRoutingFilter 是 Gateway 中非常关键的过滤器。

它的核心职责是:

使用 Reactor Netty HttpClient 把请求转发到下游服务。

也就是:
#mermaid-svg-0BJfLylsZFMle4S7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0BJfLylsZFMle4S7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0BJfLylsZFMle4S7 .error-icon{fill:#552222;}#mermaid-svg-0BJfLylsZFMle4S7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0BJfLylsZFMle4S7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0BJfLylsZFMle4S7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0BJfLylsZFMle4S7 .marker.cross{stroke:#333333;}#mermaid-svg-0BJfLylsZFMle4S7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0BJfLylsZFMle4S7 p{margin:0;}#mermaid-svg-0BJfLylsZFMle4S7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0BJfLylsZFMle4S7 .cluster-label text{fill:#333;}#mermaid-svg-0BJfLylsZFMle4S7 .cluster-label span{color:#333;}#mermaid-svg-0BJfLylsZFMle4S7 .cluster-label span p{background-color:transparent;}#mermaid-svg-0BJfLylsZFMle4S7 .label text,#mermaid-svg-0BJfLylsZFMle4S7 span{fill:#333;color:#333;}#mermaid-svg-0BJfLylsZFMle4S7 .node rect,#mermaid-svg-0BJfLylsZFMle4S7 .node circle,#mermaid-svg-0BJfLylsZFMle4S7 .node ellipse,#mermaid-svg-0BJfLylsZFMle4S7 .node polygon,#mermaid-svg-0BJfLylsZFMle4S7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0BJfLylsZFMle4S7 .rough-node .label text,#mermaid-svg-0BJfLylsZFMle4S7 .node .label text,#mermaid-svg-0BJfLylsZFMle4S7 .image-shape .label,#mermaid-svg-0BJfLylsZFMle4S7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-0BJfLylsZFMle4S7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0BJfLylsZFMle4S7 .rough-node .label,#mermaid-svg-0BJfLylsZFMle4S7 .node .label,#mermaid-svg-0BJfLylsZFMle4S7 .image-shape .label,#mermaid-svg-0BJfLylsZFMle4S7 .icon-shape .label{text-align:center;}#mermaid-svg-0BJfLylsZFMle4S7 .node.clickable{cursor:pointer;}#mermaid-svg-0BJfLylsZFMle4S7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0BJfLylsZFMle4S7 .arrowheadPath{fill:#333333;}#mermaid-svg-0BJfLylsZFMle4S7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0BJfLylsZFMle4S7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0BJfLylsZFMle4S7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0BJfLylsZFMle4S7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0BJfLylsZFMle4S7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0BJfLylsZFMle4S7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0BJfLylsZFMle4S7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0BJfLylsZFMle4S7 .cluster text{fill:#333;}#mermaid-svg-0BJfLylsZFMle4S7 .cluster span{color:#333;}#mermaid-svg-0BJfLylsZFMle4S7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0BJfLylsZFMle4S7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0BJfLylsZFMle4S7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-0BJfLylsZFMle4S7 .icon-shape,#mermaid-svg-0BJfLylsZFMle4S7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0BJfLylsZFMle4S7 .icon-shape p,#mermaid-svg-0BJfLylsZFMle4S7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0BJfLylsZFMle4S7 .icon-shape .label rect,#mermaid-svg-0BJfLylsZFMle4S7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0BJfLylsZFMle4S7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0BJfLylsZFMle4S7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0BJfLylsZFMle4S7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Gateway 收到请求
构造下游请求
HttpClient 发给目标服务
拿到 ClientResponse
放入 exchange

这个 HttpClient 不是传统阻塞式 HTTP 客户端。

它来自 Reactor Netty。

底层还是:

Netty Channel

EventLoop

ConnectionProvider

Pipeline

ByteBuf

所以 Gateway 转发请求时,不是每个请求阻塞一个线程等待下游响应。

而是:

  • 发起非阻塞下游请求;
  • 等待期间 EventLoop 可以处理其他连接;
  • 下游响应回来后继续响应式链路。

这就是 Gateway 使用 WebFlux + Reactor Netty 的核心价值。

八、NettyWriteResponseFilter 做什么?

下游响应回来后,Gateway 还要写回客户端。

这个阶段由类似:

NettyWriteResponseFilter

的组件完成。

它要把下游响应体写入客户端响应。

响应体通常是一个异步数据流:

Flux

底层写回时,最终仍然会变成:

Netty Channel writeAndFlush

这就和前面第 6 篇文章连起来了:

  • writeAndFlush 不是立刻发到对端;
  • 写不动时数据会留在写队列;
  • 需要背压协调。

Gateway 是典型的:

上游读 + 下游写

下游读 + 客户端写

的 IO 编排系统。

如果客户端很慢,写回客户端就会变慢。

如果下游很慢,等待下游响应就会变长。

所以 Gateway 必须依赖非阻塞事件驱动模型,而不是让线程阻塞等待。

九、为什么 Gateway 适合 WebFlux,而 CRUD 不一定适合?

普通 CRUD 服务通常是:
#mermaid-svg-b8AFelQIJCD8ifXZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b8AFelQIJCD8ifXZ .error-icon{fill:#552222;}#mermaid-svg-b8AFelQIJCD8ifXZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b8AFelQIJCD8ifXZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b8AFelQIJCD8ifXZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b8AFelQIJCD8ifXZ .marker.cross{stroke:#333333;}#mermaid-svg-b8AFelQIJCD8ifXZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b8AFelQIJCD8ifXZ p{margin:0;}#mermaid-svg-b8AFelQIJCD8ifXZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster-label text{fill:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster-label span{color:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster-label span p{background-color:transparent;}#mermaid-svg-b8AFelQIJCD8ifXZ .label text,#mermaid-svg-b8AFelQIJCD8ifXZ span{fill:#333;color:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ .node rect,#mermaid-svg-b8AFelQIJCD8ifXZ .node circle,#mermaid-svg-b8AFelQIJCD8ifXZ .node ellipse,#mermaid-svg-b8AFelQIJCD8ifXZ .node polygon,#mermaid-svg-b8AFelQIJCD8ifXZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b8AFelQIJCD8ifXZ .rough-node .label text,#mermaid-svg-b8AFelQIJCD8ifXZ .node .label text,#mermaid-svg-b8AFelQIJCD8ifXZ .image-shape .label,#mermaid-svg-b8AFelQIJCD8ifXZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-b8AFelQIJCD8ifXZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b8AFelQIJCD8ifXZ .rough-node .label,#mermaid-svg-b8AFelQIJCD8ifXZ .node .label,#mermaid-svg-b8AFelQIJCD8ifXZ .image-shape .label,#mermaid-svg-b8AFelQIJCD8ifXZ .icon-shape .label{text-align:center;}#mermaid-svg-b8AFelQIJCD8ifXZ .node.clickable{cursor:pointer;}#mermaid-svg-b8AFelQIJCD8ifXZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b8AFelQIJCD8ifXZ .arrowheadPath{fill:#333333;}#mermaid-svg-b8AFelQIJCD8ifXZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b8AFelQIJCD8ifXZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b8AFelQIJCD8ifXZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b8AFelQIJCD8ifXZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b8AFelQIJCD8ifXZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b8AFelQIJCD8ifXZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster text{fill:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ .cluster span{color:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-b8AFelQIJCD8ifXZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b8AFelQIJCD8ifXZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-b8AFelQIJCD8ifXZ .icon-shape,#mermaid-svg-b8AFelQIJCD8ifXZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b8AFelQIJCD8ifXZ .icon-shape p,#mermaid-svg-b8AFelQIJCD8ifXZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b8AFelQIJCD8ifXZ .icon-shape .label rect,#mermaid-svg-b8AFelQIJCD8ifXZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b8AFelQIJCD8ifXZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b8AFelQIJCD8ifXZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b8AFelQIJCD8ifXZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP 请求
查数据库
执行业务逻辑
返回 JSON

很多依赖仍然是阻塞的:

JDBC

MyBatis

JPA

同步 Redis 客户端

同步第三方 SDK

这时用 WebFlux 不一定有明显收益。

因为瓶颈可能在:

数据库

SQL

事务

业务计算

外部阻塞调用

而 Gateway 的场景不同。

Gateway 主要做:

路由

过滤

转发

等待下游

写回客户端

它大量时间花在:

  • 等待客户端 IO
  • 等待下游 IO
  • 等待网络传输

这就是 WebFlux + Reactor Netty 的优势场景。

一句话:

  • WebFlux 不一定让 CRUD 更快;
  • 但它非常适合网关这种 IO 编排型系统。

十、为什么不能在 Gateway Filter 里阻塞?

Gateway Filter 很容易被写成这样:

java 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    User user = userService.queryUser(exchange); // 阻塞调用
    return chain.filter(exchange);
}

如果这个代码运行在 EventLoop 线程上,就会出问题。

因为 EventLoop 不是当前请求独占的。

它可能负责很多连接。

阻塞一个 Filter,相当于阻塞这个 EventLoop 上的其他连接。

后果可能是:

整体延迟升高

吞吐下降

连接堆积

超时增加

下游响应处理变慢

如果必须调用阻塞逻辑,至少要切到专门线程池:

java 复制代码
return Mono.fromCallable(() -> userService.queryUser(exchange))
        .subscribeOn(Schedulers.boundedElastic())
        .flatMap(user -> chain.filter(exchange));

但这也意味着:

你把阻塞逻辑转移到了额外线程池。

它不是免费的。

所以更好的做法是:

Gateway Filter 尽量保持轻量、非阻塞。

复杂业务逻辑不应该塞进网关。

十一、Reactor Context 和 ThreadLocal 的变化

传统 Spring MVC 中,请求通常绑定一个线程。

所以很多上下文依赖:

  • ThreadLocal
  • MDC
  • SecurityContextHolder
  • RequestContextHolder

WebFlux / Reactor 模型不同。

一个请求可能在不同线程之间切换。

所以响应式链路更依赖:

Reactor Context

可以理解为:

  • 上下文跟 reactive chain 走,
  • 而不是跟线程走。

这也是 WebFlux 和 Gateway 难用的原因之一。

它不只是 IO 模型变了。

还改变了:

上下文传播

日志 trace

安全上下文

异常处理

调试方式

所以普通业务系统盲目迁移 WebFlux,经常收益不明显。

但 Gateway 这种基础设施型系统,收益更容易对上。

十二、Reactor Netty 和 Netty Pipeline 的关系

Reactor Netty 并没有取消 Netty Pipeline。

相反,它会在底层 Channel 上配置一系列 Handler。

例如:

HTTP 编解码 Handler

SSL Handler

超时 Handler

日志 Handler

Reactive Bridge Handler

Reactor Netty 官方文档中也展示了可以通过回调扩展底层 Netty pipeline,例如在连接建立时添加 ReadTimeoutHandlerLoggingHandler

这说明:

Reactor Netty 是站在 Netty Pipeline 机制上的。

只是大部分时候,普通 WebFlux 用户不直接操作这些 Handler。

他们面对的是:

  • HttpServer
  • HttpClient
  • Mono
  • Flux

而框架内部负责把这些响应式 API 映射到底层 Netty 事件。

十三、Gateway 的连接池和下游连接

作为反向代理,Gateway 不只是接收客户端连接。

它还要作为客户端访问下游服务。

这会涉及:

下游连接池

连接复用

连接超时

响应超时

最大连接数

pending acquire

这些能力通常由 Reactor Netty 的:

  • HttpClient
  • ConnectionProvider

提供。

如果下游服务慢,连接池可能耗尽。

如果 Gateway 继续接收大量请求,就会出现:

  • 等待连接
  • 请求堆积
  • 超时增加
  • 内存压力上升

所以 Gateway 性能调优不能只看 EventLoop。

还要看:

下游连接池

超时时间

重试策略

限流策略

熔断策略

请求体大小

响应体大小

高性能网关不是只靠 Netty。

它需要整套流量控制。

十四、从一条请求看完整链路

现在把一条 Gateway 请求串起来:
#mermaid-svg-eVjYEksBZtHf6O1y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eVjYEksBZtHf6O1y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eVjYEksBZtHf6O1y .error-icon{fill:#552222;}#mermaid-svg-eVjYEksBZtHf6O1y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eVjYEksBZtHf6O1y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eVjYEksBZtHf6O1y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eVjYEksBZtHf6O1y .marker.cross{stroke:#333333;}#mermaid-svg-eVjYEksBZtHf6O1y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eVjYEksBZtHf6O1y p{margin:0;}#mermaid-svg-eVjYEksBZtHf6O1y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eVjYEksBZtHf6O1y .cluster-label text{fill:#333;}#mermaid-svg-eVjYEksBZtHf6O1y .cluster-label span{color:#333;}#mermaid-svg-eVjYEksBZtHf6O1y .cluster-label span p{background-color:transparent;}#mermaid-svg-eVjYEksBZtHf6O1y .label text,#mermaid-svg-eVjYEksBZtHf6O1y span{fill:#333;color:#333;}#mermaid-svg-eVjYEksBZtHf6O1y .node rect,#mermaid-svg-eVjYEksBZtHf6O1y .node circle,#mermaid-svg-eVjYEksBZtHf6O1y .node ellipse,#mermaid-svg-eVjYEksBZtHf6O1y .node polygon,#mermaid-svg-eVjYEksBZtHf6O1y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eVjYEksBZtHf6O1y .rough-node .label text,#mermaid-svg-eVjYEksBZtHf6O1y .node .label text,#mermaid-svg-eVjYEksBZtHf6O1y .image-shape .label,#mermaid-svg-eVjYEksBZtHf6O1y .icon-shape .label{text-anchor:middle;}#mermaid-svg-eVjYEksBZtHf6O1y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eVjYEksBZtHf6O1y .rough-node .label,#mermaid-svg-eVjYEksBZtHf6O1y .node .label,#mermaid-svg-eVjYEksBZtHf6O1y .image-shape .label,#mermaid-svg-eVjYEksBZtHf6O1y .icon-shape .label{text-align:center;}#mermaid-svg-eVjYEksBZtHf6O1y .node.clickable{cursor:pointer;}#mermaid-svg-eVjYEksBZtHf6O1y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eVjYEksBZtHf6O1y .arrowheadPath{fill:#333333;}#mermaid-svg-eVjYEksBZtHf6O1y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eVjYEksBZtHf6O1y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eVjYEksBZtHf6O1y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eVjYEksBZtHf6O1y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eVjYEksBZtHf6O1y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eVjYEksBZtHf6O1y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eVjYEksBZtHf6O1y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eVjYEksBZtHf6O1y .cluster text{fill:#333;}#mermaid-svg-eVjYEksBZtHf6O1y .cluster span{color:#333;}#mermaid-svg-eVjYEksBZtHf6O1y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eVjYEksBZtHf6O1y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eVjYEksBZtHf6O1y rect.text{fill:none;stroke-width:0;}#mermaid-svg-eVjYEksBZtHf6O1y .icon-shape,#mermaid-svg-eVjYEksBZtHf6O1y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eVjYEksBZtHf6O1y .icon-shape p,#mermaid-svg-eVjYEksBZtHf6O1y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eVjYEksBZtHf6O1y .icon-shape .label rect,#mermaid-svg-eVjYEksBZtHf6O1y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eVjYEksBZtHf6O1y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eVjYEksBZtHf6O1y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eVjYEksBZtHf6O1y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端请求到达
Netty EventLoop 接收连接事件
Reactor Netty 将请求转成响应式流
WebFlux 创建 ServerWebExchange
Gateway 匹配 Route
执行 Predicate 和 Filter 链
NettyRoutingFilter 使用 Reactor Netty HttpClient 转发到下游
下游服务响应
响应体以 Flux 形式返回
NettyWriteResponseFilter 写回客户端
底层 Netty Channel writeAndFlush

这条链路本质上是在协调两个方向的 IO:

客户端 <-> Gateway

Gateway <-> 下游服务

而不是在某个线程里同步等待下游响应。

这就是 Gateway 使用 Netty 事件模型的价值。

十五、为什么理解 Netty 后才能理解 Gateway 性能?

如果只知道 Gateway 是 WebFlux 写的,会停留在:

  • 响应式
  • 非阻塞
  • 高并发

这些词上。

但真正的性能问题往往发生在更具体的地方:

EventLoop 是否被阻塞?

ChannelOutboundBuffer 是否堆积?

下游连接池是否耗尽?

请求体是否被全量缓存?

响应体是否背压失效?

Filter 是否做了阻塞调用?

ByteBuf/DataBuffer 是否泄漏?

这些问题都和 Netty 的底层模型有关。

所以学习 Netty 对 Gateway 很有价值。

它让你能看懂:

为什么不能阻塞 EventLoop

为什么慢客户端会导致写队列堆积

为什么下游慢会拖住连接池

为什么大请求体要谨慎缓存

为什么 DataBuffer 也有释放问题

十六、结论

WebFlux、Reactor Netty、Spring Cloud Gateway、Netty 的关系可以总结成:

WebFlux:

Spring 的响应式 Web 编程模型。

Reactor:

响应式流框架,提供 Mono / Flux。

Reactor Netty:

基于 Netty 的响应式网络运行时。

Netty:

底层高性能网络框架,负责 Channel、EventLoop、Pipeline、ByteBuf。

Spring Cloud Gateway:

基于 WebFlux + Reactor Netty 的网关/反向代理框架。

WebFlux 不等于 Netty。

但经典 Spring Cloud Gateway Server WebFlux 明确依赖 Netty runtime。

Gateway 之所以适合这套模型,是因为它本质上是:

IO 编排型系统。

它大量时间花在:

等待客户端

等待下游

转发请求体

转发响应体

处理连接和超时

所以它需要的不是一个请求一个线程的同步等待模型,而是:

少量 EventLoop 管理大量连接和 IO 事件。

理解了 Netty,再看 Spring Cloud Gateway,就能从:

这是一个响应式网关

进一步看到:

它底层是 Netty EventLoop、ChannelPipeline、HttpClient、写队列和背压在协作。

这才是真正的性能模型。

对我的架构判断有什么用?

这篇文章可以用来训练一个很重要的判断:网关不是"多加一层转发",而是系统 IO 压力、协议边界和治理能力的集中点

在真实系统里,HTTP 网关、MQTT 网关、媒体网关虽然名字都叫 Gateway,但关注点并不一样:

网关类型 核心职责 最怕的问题
HTTP 网关 路由、鉴权、限流、转发、响应聚合 Filter 阻塞、下游慢、连接池耗尽
MQTT Gateway 主题订阅、消息转发、协议适配、反回环 重复消费、消息风暴、来源不清、协议混乱
媒体网关 拉流、推流、转封装、播放协议适配 带宽打满、弱网卡顿、编码兼容、资源泄漏
文件上传入口 元数据校验、上传编排、对象存储写入 大文件拖垮普通请求、失败恢复不清晰

所以设计网关时,我不会只问"能不能转发",而会继续问:

  • 这是控制面流量、数据面流量,还是媒体流量?
  • 它适合走 HTTP、MQTT、WebSocket,还是专门的媒体协议?
  • 网关层是否允许做业务聚合,还是只做协议治理?
  • 有没有把慢下游、慢客户端、大请求体和高频消息隔离开?
  • 网关的线程模型、连接池、超时、限流、背压是否能解释线上现象?

能回答这些问题,才说明我们不是只会"搭一个 Gateway",而是在设计一个能承受真实流量和真实故障的接入层。

参考资料

相关推荐
砍材农夫2 天前
物联网实战:Spring Boot + Netty 搭建 MQTT | MQTT 设备模拟器
java·spring boot·后端·物联网·struts·spring·netty
不懂的浪漫3 天前
05|Netty ByteBuf 源码分析:为什么不用 Java ByteBuffer
java·netty
不懂的浪漫3 天前
07|Netty 如何处理半包粘包:ByteToMessageDecoder 源码主线
netty
砍材农夫6 天前
物联网 基于netty核心实战-心跳保活机制
java·后端·物联网·struts·servlet·netty
砍材农夫7 天前
物联网 基于netty核心实战-握手与认证
java·后端·物联网·struts·netty
997087 天前
LengthFieldBasedFrameDecoder
netty
砍材农夫11 天前
物联网 基于netty构建mqtt协议规范(遗嘱与保留消息)
java·开发语言·物联网·netty
砍材农夫12 天前
物联网 基于netty构建mqtt协议规范(主题通配符订阅)
java·前端·javascript·物联网·netty