Reactor Flux 背压机制与背压 vs 轮询
更新时间:2026-09-18。背压是响应式编程里最容易被误解的概念。它和事件循环阻塞排查、限流、async 与阻塞 IO 的区别同属异步可靠性主题。
一、什么是背压
在生产者-消费者模型里,如果生产者比消费者快,无限制地推送会让数据在中间无限堆积,最终 OOM。背压(backpressure) 是消费者把「我现在能处理多少」反向告知生产者的流控信号——需求(demand)从下游往上游传,数据按需求从上往下推。

核心机制(Reactive Streams 规范):订阅后下游调 subscription.request(n) 声明还能收 n 个,上游 onNext 发出的数量不超过累计需求;处理完再继续 request,形成由消费速率决定生产速率的闭环。
二、背压 vs 轮询
| 维度 | 背压(push + demand) | 轮询(pull/polling) |
|---|---|---|
| 驱动方 | 事件推送,但数量受下游需求约束 | 消费者主动定时/循环去拉 |
| 延迟 | 就绪即推,延迟低 | 受轮询间隔影响,有等待 |
| 资源 | 无数据时不空转 | 可能空轮询浪费,或间隔过长 |
| 典型 | Reactor/Flux、RxJava、gRPC stream | Kafka consumer.poll、定时任务、队列拉取 |
背压不是「把推送改成轮询」,而是在推送模型上叠加需求信号,兼具低延迟与不堆积。
三、Reactor 中的背压策略
当上游发得快、且无法通过 request 直接约束(如热数据源/定时器)时,用 onBackpressureXxx 明确超额数据怎么办:
flux.onBackpressureBuffer(1000) // 有界缓冲(满了再按下游策略)
.onBackpressureDrop(d -> metrics.drop(d)) // 超额直接丢弃(适合可丢的指标/采样)
.onBackpressureLatest() // 只保留最新值(适合 UI/最新状态)
.onBackpressureError() // 直接抛 Exceptions.failWithOverflow
.subscribeOn(Schedulers.boundedElastic())
.subscribe(this::handle);| 策略 | 行为 | 适用 |
|---|---|---|
| Buffer | 排队(务必有界) | 不能丢、短时突发 |
| Drop | 丢超额项 | 日志、埋点、可牺牲流量 |
| Latest | 只留最新 | 只要当前状态(进度、传感器) |
| Error | 溢出报错 | 需要显式失败暴露问题 |
publishOn/subscribeOn 切换线程后,算子内部队列默认有界(Queues 小缓冲),下游慢会自然反压上游;flatMap 可配并发度限流,parallel().runOn() 并行处理。
四、WebFlux / 全异步链路要点
- 背压要端到端才有效:Controller 返回 Flux、到响应式数据库客户端(R2DBC)、下游 WebClient 都支持响应式,需求才能一路传导。
- 链路里混入阻塞调用(JDBC、
block()、同步 HTTP、Thread.sleep)会占死少量事件循环线程,背压信号也无从传导,等同退化成阻塞系统——这是 WebFlux 最常见的事故。阻塞活放boundedElastic。 - 对外部非响应式源(消息队列、传统 RPC),在适配层用有界队列+拉取速率把它「响应式化」,而不是无界 buffer。
五、常见坑
- 用无界 buffer 假装修好背压:
onBackpressureBuffer()不给上限只是把 OOM 推迟,必须有界并配合满时策略。 - 在 map/filter 里写阻塞 IO:阻塞事件循环,吞吐崩塌且背压失效,见事件循环阻塞。
- 热数据源误用 Buffer:点击流/行情这类只增不减的实时流,慢消费者应 Drop/Latest 而非无限缓存。
- 以为换了 WebFlux 自动高性能:响应式只在全链路非阻塞且背压贯通时才有价值,半同步半反应反而更难调。
- 不压测溢出路径:下游变慢时到底是丢、排还是报错要压测验证(配合 wrk/jstack 看事件循环是否被占满)。
- request 粒度过大:一次性 request(Long.MAX_VALUE) 等于关闭背压,分批按处理能力 request 才能真正流控。
总结
背压是在推送模型上让慢消费者通过 Reactive Streams 的 request(n) 需求信号反向约束快生产者,使发出的数据不超过下游处理能力,从而避免中间无界堆积和 OOM,它不是把推送改成轮询——轮询由消费者定时拉取、有空转或延迟,而背压兼具就绪即推的低延迟和由消费速率决定生产速率的流控;Reactor 中对无法直接约束的热数据源要用有界的 onBackpressureBuffer,或对可丢数据 Drop、只关心最新值 Latest、要显式暴露溢出则 Error,并借助 publishOn 的有界内部队列和 flatMap 并发度自然传导需求;关键纪律是缓冲必须有界、绝不能在响应式链路里写阻塞调用(否则占死事件循环并使背压失效,阻塞活放 boundedElastic)、WebFlux 必须端到端非阻塞,并用压测验证下游变慢时的丢弃/排队行为。