WebFlux 集成

共 43 题
📑 题目列表 43 题
#
★★★

1. DataBuffer 使用引用计数时,丢弃、聚合或异常路径为何需要显式释放,泄漏日志如何定位

Spring WebFlux 的 DataBuffer 使用引用计数时,为什么在丢弃、聚合或异常路径需要显式释放?泄漏日志如何定位?

  • DataBuffer 的引用计数(refCnt)与内存池(Netty 直接内存)机制
  • 丢弃/聚合/异常路径下调用 release 的必要性
  • LeakSanitizer 与泄漏日志的定位

DataBuffer 是 WebFlux 中承载请求/响应字节流的抽象,底层常使用 Netty 的 PooledByteBuf,采用引用计数管理内存。当引用计数归零时缓冲区才被回收到内存池,否则内存无法释放。在正常路径,框架会自动释放;但在丢弃缓冲、聚合(如把多个 buffer 合并)、异常跳转等路径,若不显式调用 DataBufferUtils.release(buffer) 或 release() 使引用计数归零,就会造成内存泄漏(尤其是直接内存)。Netty 提供 LeakDetector(泄漏检测器),当检测到未释放的 ByteBuf 时会打印包含 "LEAK: ...ByteBuf.release() was not called" 的泄漏日志,并给出分配时的堆栈跟踪,用于定位泄漏点。排查时借助泄漏日志中的堆栈,结合 DataBufferUtils.release 的正确调用,确保所有路径释放引用。

DataBuffer 的引用计数是"内存池安全"的关键。响应式请求处理中数据流可能被分流、聚合、丢弃,每个分支都要正确管理引用计数。泄漏日志(LeakDetector)是定位泄漏的利器,它会记录分配栈。生产环境建议开启泄漏检测用于排障。

#
★★★

2. R2DBC 事务只能覆盖同一响应式连接上的操作,跨消息或远程服务时应怎样保证一致性

R2DBC 事务只能覆盖同一响应式连接上的操作,那么跨消息或跨远程服务时如何保证一致性?

  • R2DBC 事务与连接绑定的限制
  • 跨连接/跨服务的事务一致性方案(Saga、Outbox、两阶段提交)
  • 通过事务边界设计与补偿机制

R2DBC 的响应式事务(DatabaseClient 的 inTransaction)绑定在单个连接上,事务内的所有操作必须复用同一连接,因此无法在单个响应式事务中直接覆盖多个连接或多个远程服务。要保证跨消息或跨服务的一致性,需要采用分布式事务方案:一是 Saga 模式,把长事务拆分为多个本地事务,每个服务在自己连接上执行,失败时通过补偿事务回滚之前步骤;二是 Outbox 模式,把"写业务数据"与"发消息"放在同一数据库事务中(同一连接),由消息中间件异步消费,保证至少一次投递;三是事务消息(如 RocketMQ 事务消息)。选择取决于一致性要求:强一致多数据源可考虑 XA/两阶段提交(但响应式场景较少),典型业务用 Saga + Outbox 结合保证最终一致性。

核心限制是"R2DBC 事务 = 单连接内",跨边界无法用本地事务达成强一致。因此必须上升到"分布式事务"层面:Outbox 解决"数据库+消息"一致性,Saga 解决"多服务"一致性。响应式场景更偏好最终一致性方案。

#
★★★

3. Spring Boot 使用虚拟线程后,WebFlux 是否自动转为每请求一线程,二者混用应依据什么

Spring Boot 使用虚拟线程后,WebFlux 是否会自动转为每请求一线程?二者混用应依据什么?

  • WebFlux 基于 Netty 事件循环,与虚拟线程模型的差异
  • 虚拟线程只作用于 Servlet 容器(Tomcat)的阻塞模型
  • 混用依据:阻塞调用 vs 响应式调用

不会。Spring Boot 开启虚拟线程(spring.threads.virtual.enabled=true)只影响基于 Servlet 的阻塞式 MVC 容器(Tomcat/Jetty),每个请求由虚拟线程处理,可阻塞。而 WebFlux 基于 Netty 事件循环,采用非阻塞响应式模型,不会为每个请求分配一个线程,虚拟线程开关对 WebFlux 的请求处理模型不生效。二者混用的依据是任务性质:阻塞式 IO(JDBC、同步外部调用、文件操作)适合放虚拟线程或 boundedElastic;非阻塞、需背压、流式处理的场景用 WebFlux。混用时通常把 WebFlux 的阻塞调用用 subscribeOn(boundedElastic) 或虚拟线程调度器隔离,避免阻塞 Netty 事件循环。选择标准是"是否愿意为阻塞式编程的简单性付出不阻塞事件循环的成本"。

关键认知:虚拟线程与 WebFlux 是两套不同的并发模型。虚拟线程目标是"让阻塞代码并发",WebFlux 目标是"非阻塞异步"。混用时要明确每段代码的性质,不让阻塞调用污染 Netty 事件循环。

#
★★★

4. Spring WebFlux 6.1+ 在 Spring Boot 4.0 中是否已默认开启虚拟线程兼容模式

Spring WebFlux 6.1+ 在 Spring Boot 4.0 中是否已默认开启虚拟线程兼容模式?

  • Spring Framework 6.1 与 Boot 4.0 的虚拟线程支持
  • 是否默认开启(还是需显式配置)
  • 对 WebFlux 的影响

Spring Framework 6.1 增加了对虚拟线程的支持,Spring Boot 3.2/4.0 也支持通过配置开启虚拟线程,但主要面向 Servlet 容器与阻塞任务。针对 WebFlux,Spring 提供了"虚拟线程可选的兼容支持",但并未默认强制开启"WebFlux 运行在虚拟线程上"的模式——WebFlux 仍默认使用 Netty 事件循环。如果需要让 WebFlux 中的阻塞任务跑在虚拟线程上,需要显式配置(如配置一个基于虚拟线程的 Executor 或调度器,并在代码中通过 subscribeOn 使用它)。因此准确说法是:Boot 4.0 提供了虚拟线程兼容能力,但 WebFlux 默认不自动转为虚拟线程模型,需显式启用并指定。

"是否默认开启"要区分"框架支持虚拟线程"与"WebFlux 默认跑到虚拟线程"。前者是支持的,后者默认不开启。WebFlux 的响应式模型与虚拟线程是互补而非替代,默认保持 Netty 事件循环。

#
★★★

5. Spring WebFlux 6.1+ 的 WebClient 与 JDK 21 HttpClient 在 Spring Boot 4.0 中的性能对比

Spring WebFlux 的 WebClient 与 JDK 21 的 HttpClient 在 Spring Boot 4.0 中的性能对比如何?如何选择?

  • WebClient 基于 Reactor + Netty 的响应式客户端
  • JDK HttpClient 基于 CompletableFuture/虚拟线程的阻塞式客户端
  • 性能差异与适用场景

WebClient 是基于 Reactor 的响应式 HTTP 客户端,底层默认 Netty 事件循环,适合与 WebFlux 无缝衔接,支持背压、流式、组合操作符,非阻塞高并发。JDK 21 HttpClient 是 JDK 内置的 HTTP 客户端,支持 CompletableFuture 异步与虚拟线程,编程模型更贴近阻塞式/同步,配合虚拟线程也能达到高并发。性能对比要结合场景:在纯非阻塞、高吞吐、流式请求场景,WebClient(Netty)通常因事件循环与背压优势表现更优;在网络延迟主导、连接数可控、开发者偏好同步写法的场景,JDK HttpClient + 虚拟线程也能表现良好且更易维护。Boot 4.0 中 WebClient 可通过替换 ClientHttpConnector 使用 JDK HttpClient 底层。选择取决于团队熟悉度、是否需响应式组合、以及资源模型偏好。

两者都能实现高并发,但模型不同:WebClient 强调响应式异步与背压,JDK HttpClient+虚拟线程强调同步写法的简单。性能强相关于场景与底层连接器,不能一概而论,应结合读写模型与团队技术栈选择。

#
★★★

6. Spring WebFlux 6.x 中 onErrorContinue 与 onErrorResume 在微服务降级链中的取舍

Spring WebFlux 6.x 中,onErrorContinue 与 onErrorResume 在微服务降级链中应如何取舍?

  • onErrorResume 的降级切换(切换到备用数据源)
  • onErrorContinue 的跳过单元素继续
  • 降级链中两者的组合与边界

在微服务降级链中,onErrorResume 用于"整体降级":当主服务调用失败时,切换到备用数据源(如缓存、降级服务、默认值),适用于整个请求/流的降级。onErrorContinue 用于"单元素容错":在流式处理(如批量处理某服务返回的多个元素)中,某个元素处理失败时跳过它继续处理其余,适用于"逐条容错而非整体降级"。取舍原则:若是整体请求失败需要降级,用 onErrorResume 在调用层切换;若是批量元素中个别失败且不希望中断整批,用 onErrorContinue 在元素处理层跳过。注意 onErrorContinue 会掩盖错误、改变语义,应谨慎在关键路径使用,且需明确哪些错误可跳过。降级链中常两者结合:外层 onErrorResume 做整体降级,内层对可容忍的批量元素用 onErrorContinue。

两者应对的是"降级粒度"问题:onErrorResume 是请求级整体降级,onErrorContinue 是元素级跳过容错。选择依据是"失败影响范围是整体还是单元素",以及是否可接受部分成功。

#
★★

7. Spring WebFlux 6.x 在 Spring Boot Actuator 中暴露响应式指标(/actuator/metrics)

Spring WebFlux 在 Spring Boot Actuator 中如何暴露响应式指标?/actuator/metrics 能提供哪些指标?

  • WebFlux 的 Micrometer 指标(http.server.requests、reactive 相关)
  • Actuator 暴露指标的方式
  • Netty 与连接池等指标

WebFlux 基于 Micrometer 生成响应式指标,通过 Spring Boot Actuator 的 /actuator/metrics 端点暴露。常见指标包括:http.server.requests(HTTP 请求次数、耗时、状态码分布)、http.client.requests(WebClient 请求)、reactive 相关线程/队列指标、Netty 连接数、以及 R2DBC 连接池指标(r2dbc.*)等。启用方式是在 pom 中加入 micrometer + actuator 依赖,并配置 management.endpoints.web.exposure.include=metrics 暴露端点。响应式指标可用于监控吞吐、延迟、错误率与资源使用。需要注意响应式指标与 MVC 指标命名/语义略有差异,且需结合 Prometheus 等采集端做可视化。

Actuator 将 Micrometer 注册的响应式指标暴露为 HTTP 端点。WebFlux 自动注册 http.server.requests 等指标,配合数据库连接池、Netty 连接指标可全面观测响应式应用。指标暴露是响应式可观测性的基础。

#
★★

8. Spring WebFlux 与 R2DBC 在 Spring Boot 4.0 中实现全栈响应式的数据库连接池选择建议

Spring WebFlux 与 R2DBC 在 Spring Boot 4.0 中实现全栈响应式时,数据库连接池应如何选择?

  • R2DBC 连接池(r2dbc-pool)的配置
  • 连接池大小与数据库连接数、背压的匹配
  • 与其他连接池(如 HikariCP 阻塞)的对比

全栈响应式(WebFlux + R2DBC)应使用 R2DBC 的响应式连接池,即 r2dbc-pool(ConnectionPool、ConnectionPoolConfiguration),而不是 HikariCP(那是阻塞式 JDBC 连接池,会引入阻塞)。r2dbc-pool 提供异步获取连接、最大连接数、最小空闲、连接回收等参数,且连接获取/释放是响应式的,不阻塞事件循环。选择建议:连接池最大连接数要结合数据库实际连接数上限、并发请求量与背压设置;避免连接池过大导致数据库连接耗尽,也避免过小导致等待;配置连接校验、空闲超时、泄漏检测(leak detection)以保障连接健康。连接池大小需要按并发与数据库能力调优,并监控连接池指标。

全栈响应式要求从数据层到表示层都是非阻塞,因此连接池必须用响应式的 r2dbc-pool。连接池是"响应式全栈"的关键基础设施,配置需与背压、并发量、数据库连接上限匹配。

#
★★

9. Spring WebFlux 的 RouterFunction 与传统 @RequestMapping 在 Spring Boot 4.0 中的混合使用策略

Spring WebFlux 的 RouterFunction 与传统 @RequestMapping 注解在 Spring Boot 4.0 中如何混合使用?

  • RouterFunction 函数式路由与 @RequestMapping 注解路由
  • 两者在 WebFlux 中的共存机制
  • 混合使用的策略

WebFlux 支持两种端点定义方式:注解式 @RequestMapping(@GetMapping、@PostMapping 等)与函数式 RouterFunction。两者在 WebFlux 中通过不同的 HandlerMapping/HandlerAdapter 支持,可以共存于同一应用。混合使用策略:复杂、可动态组装、需要程序化路由的端点用 RouterFunction(如按条件路由、从配置读取路由);简单、静态、团队熟悉的端点用 @RequestMapping。注意:WebFlux 的 @RequestMapping 返回值是 Mono/Flux(响应式),与 MVC 的同步返回不同;函数式路由用 RouterFunction 组合 build,配合 HandlerFunction 与 ServerRequest/ServerResponse。混合时需保证路由不冲突,同一 URI 时 RouterFunction 的优先级高于注解路由的默认优先级。

两者是 WebFlux 的两种端点风格,可共存。RouterFunction 适合动态/程序化路由,注解式适合静态声明。混合使用需注意优先级与返回值类型,避免路由冲突。

#
★★

10. WebFlux + R2DBC 在 Spring Boot 4.0 中处理事务边界(@Transactional)

WebFlux + R2DBC 在 Spring Boot 4.0 中如何处理事务边界(@Transactional)?

  • R2DBC 响应式事务与 @Transactional 的配合
  • ReactiveTransactionManager 与事务边界的连接绑定
  • 非阻塞环境事务的注意事项

WebFlux + R2DBC 使用响应式事务管理器 ReactiveTransactionManager(如 R2dbcTransactionManager),配合 @Transactional 注解声明事务边界。与 JDBC 不同,R2DBC 事务是通过"绑定到当前响应式链的连接"实现的,事务上下文(连接)通过 Reactor Context 在当前响应式流中传播,而非线程局部变量。因此 @Transactional 必须作用在响应式方法上,且事务内的所有操作必须在同一响应式链上(同一连接)执行,不能在方法内人为切换线程或用 blocking 调用破坏连接绑定。注意:@Transactional 不能跨越多个连接或远程调用;响应式事务用 ReactiveTransactionManager 而非 DataSourceTransactionManager。事务边界需与响应式流的生命周期一致,避免阻塞或连接泄漏。

响应式事务的核心是"连接与响应式链绑定",通过 Reactor Context 传播。@Transactional + ReactiveTransactionManager 声明事务边界,但受限于单连接、单响应式链。不能随意切换线程或用阻塞调用。

#
★★

11. WebFlux 单元测试在 Spring Boot 4.0 中使用 WebTestClient 与 MockServer 的最佳实践

WebFlux 单元测试中,使用 WebTestClient 与 MockServer 的最佳实践是什么?

  • WebTestClient 绑定 mock 服务端测试
  • MockServer 模拟外部 HTTP 服务
  • 响应式测试的异步断言与超时

WebFlux 单元测试的最佳实践:用 WebTestClient 绑定到当前应用(bindToController、bindToRouterFunction 或 bindToServer)进行端到端或分层的反应式测试,WebTestClient 支持对返回 Mono/Flux 的响应做流式断言(expectBody、expectStatus、expectBodyList)。对于外部服务依赖,用 MockServer 模拟外部 HTTP 接口,避免依赖真实服务,配置 expectedRequest/respondWith 设置响应与断言请求。测试中要注意:响应式测试是异步的,需设置合理超时(webTestClient .responseTimeout);对 Flux 可用 .expectBodyList 或 StepVerifier 验证流式元素与背压;避免测试无限流悬挂(用 take 限制)。最佳实践是 WebTestClient 测应用行为,MockServer 隔离外部依赖,结合 StepVerifier 验证数据流。

WebTestClient 是响应式测试的核心工具,MockServer 负责外部依赖隔离。异步测试需处理超时与流式验证,防止悬挂。分层测试(controller 层用 bindToController 避免整栈启动)能提升测试速度。

#
★★

12. WebFlux 在 Spring Cloud Gateway 中的 Netty EventLoop 与 Spring 异步任务调度的边界

WebFlux 在 Spring Cloud Gateway 中,Netty EventLoop 与 Spring 异步任务调度的边界在哪里?

  • Gateway 基于 Netty EventLoop 处理请求
  • 阻塞调用与 EventLoop 的冲突
  • 异步任务调度(boundedElastic)与 EventLoop 的分工

Spring Cloud Gateway 基于 WebFlux(Netty),请求由 Netty EventLoop 线程处理,EventLoop 必须保持非阻塞,否则会阻塞所有其他请求。因此,网关中的任何阻塞操作(如同步 JDBC、阻塞式外部调用、文件 IO)都不能直接放在 EventLoop 上,必须通过 subscribeOn/调度器(如 boundedElastic)迁到其他线程执行。边界原则:EventLoop 负责网络 IO 与响应式管线(非阻塞),阻塞/计算密集任务通过调度器迁到工作线程池。Spring 异步任务调度(@Async、TaskExecutor)与 Netty EventLoop 是两个独立体系,混用时需注意线程切换不要破坏事件循环的非阻塞性。网关的过滤器、路由断言都应保持非阻塞。

Gateway 的核心约束是"EventLoop 不阻塞"。边界是"非阻塞网络管线在 EventLoop,阻塞任务在调度器线程"。任何阻塞都会拖垮整个网关,因此阻塞任务必须显式迁移线程。

#
★★

13. WebFlux 在 Spring Cloud Sleuth + OpenTelemetry 链路追踪中的上下文传播与同步阻塞差异

WebFlux 在 Spring Cloud Sleuth + OpenTelemetry 链路追踪中,响应式上下文传播与同步阻塞有何差异?

  • 响应式上下文通过 Reactor Context 传播(非线程局部)
  • 链路追踪的 traceId 在响应式中的传递
  • 与同步阻塞(ThreadLocal)的差异

在同步阻塞模型中,链路追踪的 traceId 通过 ThreadLocal 在每个线程上传递,同一个线程内所有调用共享上下文。而响应式(WebFlux)中,代码可能在多个线程间切换,且不保证同一线程,因此不能依赖 ThreadLocal,必须通过 Reactor Context 传递链路上下文。Sleuth/OpenTelemetry 的响应式集成(如 reactor 的 ContextView 装载)会在订阅时把 trace 上下文放入 Reactor Context,操作符通过 context 传递到上下游。差异在于:同步靠 ThreadLocal 隐式传递,响应式靠 Reactor Context 显式传递;若在响应式代码中误用 ThreadLocal 存 traceId,跨线程切换后会丢失。因此响应式链路追踪需使用响应式感知的上下文传播机制。

核心差异是"上下文传递介质"。同步阻塞用 ThreadLocal(线程绑定),响应式用 Reactor Context(流绑定)。响应式跨线程切换时 ThreadLocal 失效,必须用 Context 保证 traceId 沿流传播。

#
★★

14. WebTestClient 如何验证流式响应的背压、取消和超时,测试中怎样避免无限流悬挂

WebTestClient 如何验证流式响应的背压、取消和超时?测试中怎样避免无限流悬挂?

  • WebTestClient 对 Flux 响应式流的断言
  • 背压/取消/超时的验证方法
  • 防止无限流悬挂(take 限制、超时)

WebTestClient 通过 expectBodyList 或 consumeWith 对 Flux 响应式流做断言,可验证元素数量与内容。对于背压、取消等更细粒度验证,通常结合 StepVerifier:StepVerifier.create(flux) 用 expectNext、expectComplete、thenCancel 验证取消、背压(expectRequest、expectNoEvent)与超时(withVirtualTime、expectTimeout)。避免无限流悬挂的方法:一是用 take 限制无限流(如 interval 用 take(n));二是设置逐步验证的超时(withVirtualTime 模拟时间推进,或设真实超时);三是用 verifyTimeout 或 expectTimeout 防止无限等待。WebTestClient 测试流式响应时也要设 responseTimeout,避免 mock 服务无限流导致测试挂起。

WebTestClient 做整体断言,StepVerifier 做细粒度流验证(背压、取消、超时)。无限流悬挂是响应式测试的常见坑,用 take 限制 + 超时/虚拟时间避免。测试要同时验证正常流与终止/取消路径。

#
★★

15. boundedElastic 调度器有线程与队列上限,把所有阻塞任务迁入其中为何仍可能形成排队雪崩

boundedElastic 调度器有线程与队列上限,把所有阻塞任务迁入其中为何仍可能形成排队雪崩?

  • boundedElastic 线程数与队列上限
  • 阻塞任务堆积导致排队
  • 排队雪崩的成因与缓解

boundedElastic 的线程数有上限(默认 10 * CPU 核数),任务队列也有上限(默认 100000)。当阻塞任务的数量超过线程总处理能力时,超出的任务会进入队列等待;若阻塞任务本身耗时长(如慢的外部调用、锁等待),线程长期被占用,队列会持续累积,最终队列满后新任务被拒绝,且请求整体延迟剧增,形成"排队雪崩"。把所有阻塞任务都迁入 boundedElastic 并不等于解决阻塞,只是把阻塞从 EventLoop 转移到排队队列;若阻塞任务并发量大且耗时长,队列本身就成瓶颈。缓解措施:降低阻塞任务耗时(如引入缓存、限流、超时)、控制并发(限制并发数)、监控队列与线程利用率、合理设置线程数与队列上限、为不同优先级任务划分调度器。

boundedElastic 只是"隔离阻塞",不是"消除阻塞"。队列上限与线程上限决定了它的吞吐天花板,阻塞任务积压会造成排队与拒绝。缓解的关键是"减少阻塞本身"与"控制并发",而非无限迁移任务。

#
★★

16. retryWhen 对非幂等 HTTP 请求有什么风险,重试条件、退避和总截止时间应如何共同限制

retryWhen 对非幂等 HTTP 请求有什么风险?重试条件、退避和总截止时间应如何共同限制?

  • 非幂等请求(POST、扣款)重试的副作用风险
  • 重试条件(哪类错误可重试)
  • 退避(指数退避 + 抖动)与总截止时间

retryWhen 对非幂等 HTTP 请求(如 POST、下单、扣款)的风险是:重试可能导致请求被重复执行,产生重复副作用(重复扣款、重复下单)。因此非幂等操作的重试必须谨慎,或要求请求具备幂等性(如幂等键)。重试应通过 retryWhen 精确控制:重试条件(仅对可重试错误如 5xx、超时、网络错误重试,4xx 业务错误不重试);退避策略(指数退避 + 随机抖动,避免重试风暴同步触发);总截止时间(限制重试总时长,超时后放弃,防止无限重试);重试次数上限。三者共同限制:用条件过滤不可重试错误,用退避分散重试,用总截止时间兜底。对非幂等请求,通常还应配合幂等键或只对"明确可重试"的错误重试。

非幂等重试的核心风险是"副作用重复"。防护手段是幂等控制 + 重试策略限定(条件、退避、截止时间)。retryWhen 提供精细控制,相比 retry() 更安全。

#
★★

17. @RestController 返回 Mono/Flux 的工作机制

WebFlux 中 @RestController 返回 Mono/Flux 的工作机制是怎样的?

  • 返回 Mono/Flux 时框架的异步处理
  • Reactor 订阅与响应写入
  • 返回类型与响应序列化

在 WebFlux 中,@RestController 方法返回 Mono/Flux 时,框架不会阻塞等待结果,而是把返回的响应式流交给响应式处理管线:DispatcherHandler 通过 HandlerAdapter 接收到 Mono/Flux,在数据到达时订阅它,将结果异步写入响应(HttpServerResponse)。返回 Mono 对应单值响应,返回 Flux 对应流式响应(如 SSE 或 JSON 流)。实现上,框架会注册一个响应式订阅者,数据经序列化(如 Jackson)后写入响应体,onComplete 时结束响应。这样在非阻塞框架下,Java 线程不被占用,事件循环驱动整个请求处理。返回类型还决定了响应头(如 Content-Type)与流式行为。若返回 Flux 且 MediaType 为 application/stream+json,则按流式输出。

@RestController 返回 Mono/Flux 的本质是"把响应式流交给框架异步订阅与写入"。框架非阻塞订阅,数据就绪后才写响应,线程不阻塞。这是 WebFlux 高并发的核心机制。

#
★★

18. AnnotatedController 与函数式端点的对比

AnnotatedController 与函数式端点(RouterFunction)在 WebFlux 中有何对比?

  • 注解式(@Controller)与函数式(RouterFunction)的编程风格
  • 可读性、可测试性、动态路由能力
  • 各自的适用场景

AnnotatedController 使用 @Controller/@GetMapping 等注解声明端点,风格声明式、接近 Spring MVC、团队易上手、可维护性好,适合大多数 CRUD 与业务接口。函数式端点(RouterFunction + HandlerFunction)用代码组装路由,编程式、灵活,适合动态路由、条件路由、从配置/数据库生成路由、以及需要程序化组合的场景,更适合函数式编程风格或需要高度定制路由的架构。对比维度:可读性上注解式更直观;动态性上函数式更强(可运行时构建路由);可测试性两者都支持(函数式更容易测试单点 HandlerFunction);组合复用上函数式优于注解式。选择依据:常规业务用注解式,需要动态/复杂路由用函数式。两者可混合使用。

核心对比是"声明式 vs 程序式"。注解式声明简单、团队友好;函数式灵活动态、可组合。选择取决于业务复杂度与架构偏好,实际常混合使用。

#
★★

19. BlockHound 能发现哪些阻塞调用,生产环境启用前为何要评估探针开销与合法白名单

BlockHound 能发现哪些阻塞调用?生产环境启用前为何要评估探针开销与合法白名单?

  • BlockHound 检测响应式线程中的阻塞调用
  • 探针(字节码注入)的开销
  • 合法白名单(允许的阻塞)的配置

BlockHound 是一个用于检测响应式代码中阻塞调用的工具,它通过字节码注入(Java Agent)在运行时拦截某些阻塞方法(如 Thread.sleep、synchronized、套接字阻塞读、lock 等),当它们在"非阻塞线程"(如 Netty EventLoop)上被调用时抛出异常,从而帮助开发者发现阻塞事件循环的代码。BlockHound 提供 allowBlockingCallsInside 白名单,允许某些"合法阻塞"(如经授权的阻塞路径)不被误报。生产启用前需评估:一是探针开销,字节码注入与运行时拦截会带来额外性能损耗,需测试影响;二是白名单配置,需仔细甄别哪些阻塞是合法的、哪些是痛点,避免误报或漏报。生产环境应谨慎开启,先充分测试,评估开销与误报率。

BlockHound 的价值是"在开发期发现阻塞 bug",通过拦截阻塞调用定位。但探针有开销、白名单需甄别,生产启用需权衡收益与成本。它更适合作为测试/CI 的门禁而非生产常驻。

#
★★

20. RouterFunction 与函数式路由

WebFlux 的 RouterFunction 与函数式路由是如何工作的?

  • RouterFunction 的路由定义与组合
  • HandlerFunction 的请求处理
  • 路由的嵌套与过滤

RouterFunction 是函数式路由的抽象,通过 route(GET("/users")).handle(...) 定义"请求匹配条件 + 处理逻辑"的映射,多个路由可组合(route() 链式、.and()、.andRoute())。HandlerFunction 是处理请求的函数,接收 ServerRequest 返回 ServerResponse(Mono)。函数式路由支持嵌套路由(nest)与路由过滤(RouterFunction.filter),可对一组路由统一应用过滤器。内置 RequestPredicates 提供路径、方法、参数、header 等匹配条件。函数式路由适合动态构建、程序化组合、以及需要精细控制路由的场景。它与注解式共存,通过 RouterFunctionMapping 注册。

函数式路由的原子是"条件 + 处理器",通过组合构建路由树。它比注解式更灵活,支持嵌套、过滤、动态构建,适合复杂/动态路由场景。

#
★★

21. SSE 客户端断开后 Flux 如何感知取消并清理心跳、订阅和租户资源,怎样验证无泄漏

SSE 客户端断开后,Flux 如何感知取消并清理心跳、订阅和租户资源?怎样验证无泄漏?

  • SSE 客户端断开触发取消信号
  • doFinally/doOnCancel 清理资源(心跳、订阅、租户)
  • 验证无泄漏的方法(指标、测试)

SSE 客户端断开连接时,WebFlux 会取消对应的响应式流(触发 cancel 信号)。开发者可通过 doFinally(或 doOnCancel)在取消时执行清理逻辑:停止心跳定时器(interval 的 dispose)、关闭订阅的资源(如关闭线程、连接)、释放租户相关的上下文/资源(如租户数据源、缓存)。清理时需注意:心跳定时器要 dispose 避免泄漏;订阅的外部资源要释放(如 Sinks、长连接);租户上下文要清理避免线程污染。验证无泄漏的方法:用指标监控(连接数、心跳线程数、订阅数是否回落)、编写测试模拟客户端断开(用 WebTestClient/StepVerifier 的 thenCancel 触发取消)并断言清理逻辑执行、使用内存/线程 dump 检查是否有残留。

SSE 的取消是"清理钩子",doFinally/doOnCancel 是执行清理的位置。泄漏验证要通过指标与测试确认取消后资源正确回收。核心是"让取消路径也释放资源"。

#
★★

22. ServerSentEvents(SSE)与 text/event-stream

ServerSentEvents(SSE)是什么?text/event-stream 媒体类型如何工作?

  • SSE 的单向服务端推送机制
  • text/event-stream 的格式(data/event/id/retry 字段)
  • WebFlux 中返回 Flux 实现 SSE

SSE(Server-Sent Events)是一种基于 HTTP 的服务端单向推送技术,客户端通过一个长连接持续接收服务端推送的事件,媒体类型为 text/event-stream。其格式是文本流,每条事件由 data:、event:、id:、retry: 等字段组成,以空行分隔,客户端按事件解析。相比 WebSocket,SSE 单向、基于 HTTP、自动重连、支持原生 EventSource 客户端,适合服务端主动推送(如通知、进度、实时数据)。WebFlux 中实现 SSE:返回 Flux 并设置 Content-Type 为 text/event-stream,或用 ServerSentEvent 包装(带 id、event、data),框架会按 SSE 格式写到响应流。WebFlux 支持 ServerSentEvent 作为返回类型,配合 HTTP 流式响应。

SSE 是文本格式的 HTTP 流式推送,字段结构清晰。WebFlux 用 Flux + ServerSentEvent 实现 SSE。与 WebSocket 相比,SSE 单向、简单、自动重连,适合单向推送场景。

#
★★

23. Spring WebFlux 6.x 中 ServerHttpSecurity 与 Spring Security 6.4 的反应式安全配置变化

Spring WebFlux 6.x 中 ServerHttpSecurity 与 Spring Security 6.4 的反应式安全配置有什么变化?

  • ServerHttpSecurity 的响应式安全配置
  • SecurityFilterChain 的 lambda DSL 与 6.x 变更
  • 配置项(authorizeExchange、oauth2、CSRF 等)

WebFlux 中使用 ServerHttpSecurity 构建响应式安全配置(SecurityWebFilterChain),与 Spring Security 6.x 的 lambda DSL 风格一致。Spring Security 6.x 中,WebFlux 与 MVC 的安全配置统一使用 SecurityFilterChain(WebFlux 用 ServerHttpSecurity 变体),配置项包括 authorizeExchange(URL 授权)、oauth2Login/oauth2ResourceServer(OAuth2/资源服务器)、csrf(CSRF 防护)、formLogin、httpBasic 等。6.4 等版本持续推进 lambda DSL、废弃旧式配置、更新默认值(如更严格的安全默认)。在 WebFlux 中,安全配置是响应式的(基于 Mono/Flux),需注意安全上下文(ReactiveSecurityContextHolder)通过 Reactor Context 传播。配置变化主要是 DSL 简化与默认值调整,需关注官方迁移说明。

ServerHttpSecurity 是响应式安全配置入口,使用 lambda DSL。6.x 重点是 DSL 统一、默认安全收紧。响应式安全上下文通过 Reactor Context 传播,与同步 ThreadLocal 不同。

#
★★

24. Spring WebFlux 与 Spring AI 2.0 流式响应(Streaming Response)

Spring WebFlux 与 Spring AI 2.0 如何结合实现流式响应(Streaming Response)?

  • WebFlux 的 Flux 流式响应与 LLM 流式输出
  • Spring AI 的流式 API(返回 Flux)
  • 流式输出在 WebFlux 中的实现

Spring AI 2.0 的流式 API 返回 Flux<...>(如 stream() 返回 Flux 或 Flux ),天然适合 WebFlux 的流式响应。WebFlux 控制器返回 Flux 时,框架按流式(SSE 或 application/stream+json)把 LLM 的逐 token 输出推送给客户端,实现"打字机效果"的流式响应。实现要点:控制器返回 Flux 或 Flux,设置 Content-Type 为 text/event-stream 或 application/x-ndjson;处理取消(客户端断开时取消 LLM 请求);处理背压与重试。Spring AI 与 WebFlux 结合,能实现低延迟、逐 token 的流式对话体验,适合 AI 助手、实时生成等场景。

WebFlux 的 Flux 流式响应是 Spring AI 流式输出的天然载体。LLM 流式返回 Flux,WebFlux 按流式媒体类型推送,实现逐 token 输出与取消传播。

#
★★

25. Spring WebFlux 与 Spring MVC 的取舍

Spring WebFlux 与 Spring MVC 在选型上如何取舍?

  • 两者架构与编程模型差异(响应式 vs 阻塞)
  • 适用场景与团队能力
  • 全栈响应式 vs 传统 MVC

Spring MVC 基于 Servlet 阻塞式模型,编程简单、同步直观、生态成熟(JDBC、事务、模板引擎等),适合绝大多数业务场景,尤其有阻塞式依赖(JDBC、阻塞客户端)时。Spring WebFlux 基于响应式非阻塞模型,适合高并发、IO 密集、流式/背压场景,以及需要全栈响应式(R2DBC、WebClient、RSocket)的架构。取舍因素:一是并发与吞吐需求,WebFlux 在高并发 IO 场景有优势;二是依赖生态,若现有数据层是 JDBC 阻塞,用 WebFlux 收益有限;三是团队能力,响应式学习曲线陡;四是是否需要流式/背压/SSE。多数场景 MVC 足够,WebFlux 用于特定高并发或流式场景。建议优先 MVC,除非有明确响应式需求。

取舍的核心是"阻塞 vs 非阻塞与生态匹配"。MVC 成熟简单适合绝大多数业务,WebFlux 面向高并发/流式/全栈响应式。应基于需求、生态与团队能力决策,避免为响应式而响应式。

#
★★

26. Spring WebFlux 的背压(Backpressure)

Spring WebFlux 中的背压(Backpressure)是如何工作的?如何应用?

  • WebFlux 基于 Reactor 的背压机制
  • 请求处理链的背压传播
  • 流式响应/数据库查询的背压应用

WebFlux 基于 Reactor,其背压机制贯穿请求处理链:Netty 收到请求数据后,通过 Reactor 的 request(n) 信用机制控制数据发射节奏;下游处理慢时,通过取消/减少请求量反向限制上游。在 WebFlux 中,背压体现在多个层面:HTTP 请求体读取(DataBuffer 按需请求)、流式响应(Flux 按订阅者 request 节奏)、以及 R2DBC 查询(DatabaseClient 的 Flux 结果按需拉取)。应用背压时,可通过操作符(onBackpressureBuffer/Drop/Latest)、自定义 request 数量、以及数据库查询的 fetch size 控制数据量。背压保证了高并发下内存可控,避免上下游速度不匹配导致内存溢出。

WebFlux 的背压是"开箱即用"的,因为底层是 Reactor。理解 request 信用机制,就能在流式响应、数据库查询等场景控制数据流。背压是响应式内存安全的核心。

#
★★

27. WebClient 与 RestTemplate 的差异

WebClient 与 RestTemplate 在 WebFlux 中有何差异?

  • RestTemplate 阻塞式 vs WebClient 响应式
  • 异步/响应式组合能力
  • 相互替代的迁移

RestTemplate 是阻塞式 HTTP 客户端,每次调用同步阻塞等待响应,基于线程池,适合 MVC 传统模型。WebClient 是响应式 HTTP 客户端,返回 Mono/Flux,非阻塞,支持背压、流式、组合操作符(retry、重试、合并),适合 WebFlux 与响应式架构。差异核心:调用模型(阻塞 vs 非阻塞)、返回值(同步对象 vs Mono/Flux)、组合能力(WebClient 可响应式组合)、资源(RestTemplate 占线程,WebClient 占事件循环)。WebClient 可替代 RestTemplate 用于响应式场景,但若在 MVC 阻塞模型中调用,需注意 WebClient 的响应式回调与阻塞等待的配合。Spring 官方推荐新代码用 WebClient(或 RestClient 做同步风格)。

核心差异是"阻塞 vs 非阻塞"。WebClient 面向响应式,返回 Mono/Flux,可组合;RestTemplate 面向阻塞。选型取决于架构模型与是否需要响应式能力。

#
★★

28. WebClient 的 retrieve()/exchangeToMono()/exchangeToFlux()

WebClient 的 retrieve()、exchangeToMono()、exchangeToFlux() 有什么区别?各自适合什么场景?

  • retrieve 的简易取值(自动处理状态码)
  • exchangeToMono/exchangeToFlux 的底层响应访问
  • 状态码处理与响应体消费

retrieve() 是简化 API,自动把响应体解码为目标类型,非 2xx 状态码默认抛 WebClientResponseException,适合简单请求响应。exchangeToMono()/exchangeToFlux() 提供对 ClientResponse 的底层访问,开发者可自定义状态码处理、响应头读取、响应体消费逻辑,适合需要精细控制响应(如自定义错误处理、按状态分支)的场景。区别:retrieve 封装了状态码异常处理,exchange 系列暴露原始响应。注意:exchange 系列返回的 ClientResponse 的响应体只能消费一次,且必须消费(读取或释放)以避免连接泄漏;retrieve 则自动管理。用 exchange 时需手动处理响应体释放与错误分支。

retrieve 是"简化取值",exchange 是"底层控制"。选择依据是是否需要自定义状态码/响应体处理。exchange 的高级控制伴随响应体消费次数的约束,需小心释放。

#
★★

29. WebClient.exchangeToMono 自定义分支时如何确保未消费响应体被释放,遗漏会耗尽什么资源

WebClient.exchangeToMono 自定义分支时,如何确保未消费的响应体被释放?遗漏会耗尽什么资源?

  • 响应体消费一次性与释放
  • 未消费响应体导致连接/内存泄漏
  • 用 releaseBody 或 doOnNext 确保释放

WebClient.exchangeToMono 返回 ClientResponse,响应体只能消费一次。若某个分支不消费响应体(如只读响应头、或错误分支直接返回),必须显式释放响应体,否则连接无法归还连接池,造成连接泄漏(Netty 连接被占用)与内存泄漏。确保释放的方式:调用 response.releaseBody() 释放响应体(返回 Mono);或在分支中通过 bodyToMono/bodyToFlux 消费后释放;用 doFinally 确保所有分支释放。遗漏释放会耗尽连接池(连接无法复用)和直接内存(DataBuffer 未归还),最终导致"连接池满"或 OOM。因此 exchange 系列必须清楚地管理响应体生命周期。

响应体是"一次性的、需释放的资源"。exchange 自定义分支时,未消费分支必须释放响应体,否则连接与内存泄漏。releaseBody/doFinally 是确保释放的手段。

#
★★

30. WebExceptionHandler、@ControllerAdvice 与操作符 onErrorResume 各适合在哪一层转换错误

WebExceptionHandler、@ControllerAdvice 与操作符 onErrorResume 各适合在哪一层转换错误?

  • WebExceptionHandler 全局异常处理(框架层)
  • @ControllerAdvice 控制器异常处理(业务层)
  • onErrorResume 操作符级错误转换(管线层)

三层错误处理职责不同:WebExceptionHandler 是框架层的全局异常处理器,处理未经控制器处理的异常(如路由错误、序列化错误),可返回统一错误响应,适合最外层兜底。@ControllerAdvice(配合 @ExceptionHandler)是控制器/业务层的异常处理,根据异常类型返回自定义错误响应(如业务异常、校验异常),是 WebFlux 主流的业务错误映射方式。onErrorResume 是操作符级(管线层)的错误转换,在响应式流内部对错误做降级/转换,适合在数据流内部处理(如调用外部服务失败时降级),错误在到达框架前已被处理。使用建议:业务错误用 @ControllerAdvice 映射,底层降级用 onErrorResume,框架级兜底用 WebExceptionHandler。三者层级不同,可组合使用。

区分层级:WebExceptionHandler 最外层框架兜底、@ControllerAdvice 业务错误映射、onErrorResume 操作符内降级。选层依据是"错误发生在哪层、需要什么粒度处理"。

#
★★

31. WebFlux 与 R2DBC 的整合

WebFlux 与 R2DBC 是如何整合实现全栈响应式的?

  • R2DBC 响应式数据库驱动
  • DatabaseClient/R2dbcRepository 与 WebFlux 的整合
  • 全栈响应式数据流

WebFlux 与 R2DBC 整合实现从表示层到数据层的全栈响应式。R2DBC 是响应式关系数据库驱动,提供非阻塞数据库访问;WebFlux 控制器返回 Mono/Flux,数据层通过 DatabaseClient(接口式 SQL)或 R2dbcRepository(Spring Data 响应式仓库)返回 Mono/Flux,整个请求链路无阻塞。整合方式:配置 R2dbcEntityTemplate/R2dbcRepository,注入响应式 Repository 到 WebFlux 控制器,控制器方法返回 Flux/Mono 直接映射数据库查询结果。事务用 ReactiveTransactionManager。全栈响应式的优势是:请求处理线程(Netty EventLoop)不阻塞,数据库连接池(r2dbc-pool)非阻塞,吞吐高、内存可控。需注意 Spring Data 的响应式 Repository 与 WebFlux 原生集成。

全栈响应式的核心是"数据层也用响应式"。R2DBC(DatabaseClient/R2dbcRepository)返回 Mono/Flux,与 WebFlux 无缝衔接,请求链路全程非阻塞。Spring Data R2DBC 提供响应式 Repository 支持。

#
★★

32. WebFlux 与 Spring Cloud OpenFeign(阻塞式)

WebFlux 与 Spring Cloud OpenFeign(阻塞式)如何整合?阻塞式客户端在响应式中如何用?

  • OpenFeign 的阻塞式客户端特性
  • 在响应式代码中调用阻塞客户端的处理
  • 用 subscribeOn 隔离阻塞

Spring Cloud OpenFeign 是阻塞式 HTTP 客户端(基于同步调用),在 WebFlux 响应式环境调用时会阻塞当前线程。若在 Netty EventLoop 上直接调用 OpenFeign,会阻塞事件循环,破坏非阻塞性。正确做法:在 WebFlux 服务中调用 OpenFeign 时,用 subscribeOn(Schedulers.boundedElastic()) 把阻塞调用迁到弹性线程池执行,避免阻塞事件循环;或改用 WebClient/ReactiveFeign 做响应式调用。整合时注意:阻塞调用要隔离线程、设置超时、监控阻塞耗时。若追求全响应式,优先用 WebClient 或 ReactiveFeign 替代 OpenFeign。混用场景下,OpenFeign 需配合调度器隔离。

核心是"阻塞客户端不能阻塞事件循环"。OpenFeign 在 WebFlux 中需用 subscribeOn 隔离到弹性线程,或改用响应式客户端。选择取决于是否坚持全响应式。

#
★★

33. WebFlux 与 Spring Modulith 模块化架构结合时的反应式跨模块事件传播机制

WebFlux 与 Spring Modulith 模块化架构结合时,反应式跨模块事件传播机制是怎样的?

  • Spring Modulith 的应用事件发布
  • 响应式事件传播(ReactiveApplicationEventPublisher)
  • 模块化边界与事件生效

Spring Modulith 提供模块化架构支持,通过应用事件(ApplicationEvent)实现模块间通信,且支持反应式事件传播:使用 ReactiveApplicationEventPublisher 发布响应式事件,监听器返回 Mono/Flux,事件在响应式上下文中传播。在 WebFlux 应用中,模块间通过发布响应式事件解耦,A 模块发布事件,B 模块异步监听处理,事件处理可返回 Mono/Flux 实现响应式异步处理。Modulith 的事件发布还支持事件失效(event publication registry)与事件溯源。结合 WebFlux 时,事件监听器应返回响应式类型,配合事务/异步实现可靠的消息驱动。模块边界通过事件驱动解耦,保持模块内聚。

Spring Modulith + WebFlux 的整合是"响应式事件驱动模块化"。ReactiveApplicationEventPublisher 发布响应式事件,监听器返回 Mono/Flux,实现模块间异步解耦。事件发布注册表保证可靠性。

#
★★

34. WebFlux 应用优雅停机时,如何停止接收新请求并等待活动流结束,同时限制最长排空时间

WebFlux 应用优雅停机时,如何停止接收新请求并等待活动流结束,同时限制最长排空时间?

  • Spring Boot 优雅停机(graceful shutdown)配置
  • Netty 停机与活动请求排空
  • 最长排空时间限制

WebFlux 应用优雅停机通过 Spring Boot 的 server.shutdown=graceful 配置启用,应用收到停机信号后停止接收新请求,等待活动请求完成后再退出。底层是 Netty 的优雅关闭(GracefulShutdown),等待正在处理的请求/流结束。需限制最长排空时间(spring.lifecycle.timeout-per-shutdown-phase),防止活动流无限等待导致停机超时挂起。实现要点:配置 server.shutdown=graceful 与 shutdown 超时;对长连接(SSE、流式)需注意其可能持续,需在超时后强制关闭;配合自定义停机钩子(ApplicationListener)释放资源。限制排空时间可避免"卡死"停机,超时后强制终止。

优雅停机 = 停止接收新请求 + 排空活动流 + 限制最大排空时间。Netty 优雅关闭 + shutdown=graceful + timeout-per-shutdown-phase 实现。长连接需特别处理,防止无限期排空。

#
★★

35. WebFlux 测试(WebTestClient)

WebFlux 的 WebTestClient 如何进行测试?支持哪些测试模式?

  • WebTestClient 的绑定方式(bindToServer/bindToController/bindToApplicationContext)
  • 响应式断言
  • 与 MockMvc 的区别

WebTestClient 是 WebFlux 的测试客户端,支持多种绑定方式:bindToServer 连接到真实服务器(端到端)、bindToController 只测单个控制器、bindToRouterFunction 测函数式路由、bindToApplicationContext 绑定完整 Spring 上下文(集成测试)。它用流畅 API 断言响应:expectStatus、expectHeader、expectBody、expectBodyList(对 Flux 流式)、jsonPath 等。与 MockMvc 的区别:WebTestClient 是响应式测试客户端,支持异步/流式接口,而 MockMvc 面向 MVC 同步。WebTestClient 是 WebFlux 测试的首选,可结合 StepVerifier 验证流式数据。

WebTestClient 的绑定方式决定了测试粒度(从单控制器到端到端)。响应式断言支持 Mono/Flux 与流式。它是 WebFlux 测试的核心工具。

#

36. WebFlux 的 Flux 与 delayElements

WebFlux 中 Flux 与 delayElements 是如何配合实现定时/限速流式输出的?

  • Flux 的定时生成(interval)
  • delayElements 对元素添加延迟
  • 流式输出限速场景

Flux 常见于 Flux.interval() 生成的周期性递增值,用于心跳、定时轮询等。delayElements 会给流中的每个元素附加一个延迟,实现"限速输出"或"节流式流式响应",例如把一批数据按固定间隔逐个发送(如每秒发一条),适合 SSE 流式推送、限速队列等场景。两者配合:interval 生成时间序列,delayElements 控制元素间延迟,实现按节奏的流式输出。注意 delayElements 会在每个元素间插入延迟,用于模拟真实推送节奏或控制下游负载;可指定调度器。与 interval 的区别:interval 是按周期生成元素,delayElements 是给已有元素加延迟。

Flux 承载时间/序号,delayElements 控制节奏。结合用于限速流式输出。区分 interval(生成周期)与 delayElements(元素延迟)。

#

37. WebFlux 的 MediaType 与协商

WebFlux 中 MediaType 与内容协商如何工作?

  • MediaType 与 Accept 头协商
  • 返回类型与媒体类型匹配
  • 流式媒体类型(text/event-stream 等)

WebFlux 根据请求的 Accept 头与控制器返回类型进行内容协商,选择合适的 MediaType 序列化响应。控制器通过 @GetMapping(produces=...) 限定可产生的内容类型,返回类型(Mono/Flux、对象、ServerSentEvent)决定序列化方式。例如返回 Flux 且 Accept 为 text/event-stream 时按 SSE 输出;返回对象时按 JSON(application/json)。WebFlux 使用 HttpMessageWriter 进行序列化,Jackson 等支持 JSON。协商失败时返回 406 Not Acceptable。流式媒体类型如 text/event-stream、application/x-ndjson、application/stream+json 用于流式响应。理解 MediaType 协商是正确设置响应头与流式输出的关键。

内容协商 = 请求 Accept + 控制器 produces + 返回类型 → 选择序列化器。流式媒体类型配合 Flux 实现流式输出。协商决定响应 Content-Type 与序列化方式。

#

38. WebFlux 的过滤器(WebFilter)

WebFlux 的 WebFilter 是什么?如何编写与使用?

  • WebFilter 的请求处理链职责
  • 编写 WebFilter(实现接口,返回 Mono)
  • 过滤器顺序与注册

WebFilter 是 WebFlux 的响应式过滤器,用于对请求/响应做预处理和后处理,如认证、日志、请求头处理、限流。实现 WebFilter 接口,在 filter 方法中检查/修改 ServerWebExchange,然后调用 chain.filter(exchange) 继续处理链,返回 Mono 。过滤器按 @Order 或 Ordered 排序,数值小的先执行。通过 @Component 注册为 Spring Bean 自动生效。与 Servlet 的 Filter 不同,WebFilter 是响应式的,返回 Mono,不阻塞事件循环。可组合多个过滤器形成过滤链。WebFilter 用于横切关注点(认证、日志、CORS、安全)。

WebFilter 是响应式请求处理的横切点,返回 Mono 非阻塞。通过 @Order 排序、@Component 注册。用于认证、日志、安全等横切逻辑。

#

39. WebFlux 的错误处理(@ControllerAdvice + ResponseEntityExceptionHandler)

WebFlux 中如何使用 @ControllerAdvice + ResponseEntityExceptionHandler 处理错误?

  • @ControllerAdvice + @ExceptionHandler 的错误映射
  • 响应式异常处理
  • 与 ResponseEntityExceptionHandler 的关系

WebFlux 中通过 @ControllerAdvice + @ExceptionHandler 处理控制器层异常,@ExceptionHandler 方法根据异常类型返回响应式错误响应(Mono 或 ServerResponse)。ResponseEntityExceptionHandler 是 Spring MVC 提供的基类,用于处理标准 Spring 异常(如 400、404、405),在 WebFlux 中对应响应式版本(WebFlux 有对应的处理方式)。但需注意:WebFlux 的异常处理更常通过自定义 @ExceptionHandler 返回 Mono<ResponseEntity>,或实现 ErrorWebExceptionHandler 做全局兜底。@ControllerAdvice 用于业务异常映射,ResponseEntityExceptionHandler 处理框架异常,两者结合覆盖控制器层错误。WebFlux 的异常处理是响应式的,返回 Mono 而非同步。

@ControllerAdvice 是 WebFlux 业务错误映射的主要方式,@ExceptionHandler 返回响应式响应。分布式错误处理需结合全局异常处理器。WebFlux 的异常处理全程响应式。

#

40. retrieve 的 onStatus 与全局 ExchangeFilterFunction 如何分工,错误响应体只能消费一次意味着什么

WebClient 的 retrieve().onStatus() 与全局 ExchangeFilterFunction 如何分工?错误响应体只能消费一次意味着什么?

  • onStatus 对特定状态码的自定义错误处理
  • ExchangeFilterFunction 全局过滤器
  • 错误响应体只消费一次的影响

retrieve().onStatus() 针对特定状态码(如 4xx/5xx)自定义错误处理,将非 2xx 状态码映射为自定义异常,适合按状态码做局部错误处理。ExchangeFilterFunction 是全局过滤器,在请求发出前/响应返回后执行,用于跨所有请求的横切处理(认证、日志、添加 header、重试、统一错误处理),适合全局逻辑。两者分工:onStatus 做局部状态码映射,ExchangeFilterFunction 做全局横切。错误响应体只能消费一次意味着:响应体(body)是一个一次性流,如果已在某个地方消费(如 onStatus 中读取 body),就不能再被后续消费(如全局过滤器再读),否则报错或得到空。因此错误处理中要避免重复消费响应体,决定好在哪里消费一次。

onStatus 局部、ExchangeFilterFunction 全局,分层处理。响应体一次性,消费后不可复用,需在设计错误处理链时避免重复读取。这是 WebClient 错误处理的常见坑。

#

41. 使用 Spring WebFlux 实现 SSE(Server-Sent Events)

使用 Spring WebFlux 如何实现 SSE(Server-Sent Events)?

  • 返回 Flux 并设置 text/event-stream
  • ServerSentEvent 包装
  • 与心跳/取消处理

在 WebFlux 中实现 SSE:控制器返回 Flux,并设置 Content-Type 为 text/event-stream(或通过 produces 指定),框架会把 Flux 元素按 SSE 文本格式写入响应流。更完整的做法是返回 Flux<ServerSentEvent>,ServerSentEvent 包装 data、id、event、retry 等字段,支持事件命名与重连。可实现心跳(interval 定期发送注释/事件保持连接)、处理取消(客户端断开时 Flux 收到取消,doFinally 清理)。实现要点:MediaType.TEXT_EVENT_STREAM、Flux 数据源(如 interval、Sinks、数据库流)、心跳与取消清理。SSE 适合服务端单向推送实时数据。

SSE 实现 = Flux + text/event-stream + 可选 ServerSentEvent 包装。需处理心跳与取消。WebFlux 原生支持 SSE 流式输出。

#

42. 使用 application/x-ndjson 返回流式 JSON 时,每条记录的编码失败应终止全流还是发送错误事件

使用 application/x-ndjson 返回流式 JSON 时,某条记录的编码失败应终止全流还是发送错误事件?

  • ndjson 流式 JSON 的逐行编码
  • 单条编码失败的语义选择
  • 错误处理与流的终止

application/x-ndjson(newline-delimited JSON)以换行分隔的 JSON 记录流式输出。当某条记录编码失败时,是终止全流还是发送错误事件,取决于业务语义:若数据完整性要求高、失败不可容忍,应终止全流(onError 终止,客户端感知失败);若每条记录独立、允许跳过失败记录,则用 onErrorContinue 跳过该条继续输出后续记录,同时通过日志/指标记录错误。终止全流会中断后续所有记录,适合"一条失败即整体失败";跳过继续适合"逐条容错"。实际需权衡:流式输出中客户端已消费了部分记录,中途终止会让客户端处于不一致状态,因此对"已部分输出"的场景,常见做法是跳过失败记录并记录错误,或发送一个错误事件标记,而非静默终止。选择需明确错误暴露策略。

编码失败的处理是"终止 vs 跳过"的权衡,取决于数据完整性与流式消费特性。已部分输出时终止会造成不一致,逐条容错需配合错误记录。onErrorContinue 实现跳过,onError 实现终止。

#

43. 连接、响应、读取和业务总截止时间应如何组合,只有 responseTimeout 会漏掉哪些阶段

WebClient 的连接、响应、读取和业务总截止时间应如何组合?只有 responseTimeout 会漏掉哪些阶段?

  • 连接、响应、读取超时的粒度
  • 不同阶段超时配置
  • 只配 responseTimeout 的遗漏

HTTP 客户端超时应覆盖多个阶段:连接超时(connectTimeout,建立连接的时间)、响应超时(responseTimeout,从发请求到收到响应的总时间)、读取超时(readTimeout,两次读取之间的间隔)、以及业务层总截止时间(整个调用链的 deadline)。若只配置 responseTimeout,会漏掉:连接阶段超时(连接建立过长时 responseTimeout 可能覆盖,但更精确需 connectTimeout)、读取阶段停滞(响应未完成但间无数据,readTimeout 可中断)、以及业务总流程超时(responseTimeout 只针对单次 HTTP 调用,不覆盖重试/组合/整体业务截止)。WebClient 通过 reactor.netty.http.client 的 connectTimeout、responseTimeout 配置,readTimeout 需通过连接配置,业务总截止用 timeout() 操作符。合理组合:connectTimeout 管连接、responseTimeout 管整体响应、readTimeout 管读停滞、业务 timeout 管调用链总时长。

超时分层:连接、响应、读取、业务总截止各有侧重。只配 responseTimeout 会遗漏连接与读取停滞的精确控制,以及重试/组合的整体截止。需组合配置覆盖所有阶段。