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

前面几篇我们已经从 Netty 自身看过:
ServerBootstrap 启动流程
EventLoop 线程模型
Pipeline 责任链
ByteBuf 内存管理
writeAndFlush 写出与背压
半包粘包与编解码
这一篇把视角往上抬一层:
Spring WebFluxReactor NettySpring 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。
它面对的是:
MonoFluxServerWebExchange
而底层真正收发数据的仍然是:
Netty ChannelEventLoopPipelineByteBuf
五、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 中,请求通常绑定一个线程。
所以很多上下文依赖:
ThreadLocalMDCSecurityContextHolderRequestContextHolder
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,例如在连接建立时添加 ReadTimeoutHandler 或 LoggingHandler。
这说明:
Reactor Netty 是站在 Netty Pipeline 机制上的。
只是大部分时候,普通 WebFlux 用户不直接操作这些 Handler。
他们面对的是:
HttpServerHttpClientMonoFlux
而框架内部负责把这些响应式 API 映射到底层 Netty 事件。
十三、Gateway 的连接池和下游连接
作为反向代理,Gateway 不只是接收客户端连接。
它还要作为客户端访问下游服务。
这会涉及:
下游连接池
连接复用
连接超时
响应超时
最大连接数
pending acquire
这些能力通常由 Reactor Netty 的:
HttpClientConnectionProvider
提供。
如果下游服务慢,连接池可能耗尽。
如果 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",而是在设计一个能承受真实流量和真实故障的接入层。