Flow中的buffer详解

什么是buffer

  • 上游(emit)下游(collect) 之间插入一个有界/无界缓冲队列(底层是 Channel),把两端解耦:上游可以先"塞进队列就走",下游慢慢消费。
  • 主要用于背压处理并发解耦
scss 复制代码
[ emitter ] --(buffer queue)--> [ collector ]

基本用法与参数

ini 复制代码
flow.buffer(
    capacity: Int = Channel.BUFFERED,          // 常用:Channel.BUFFERED(≈64) / UNLIMITED / RENDEZVOUS(0)
    onBufferOverflow: BufferOverflow = SUSPEND // 也可:DROP_OLDEST / DROP_LATEST
)
  • SUSPEND(默认) :队列满了就挂起上游,保证不丢数据。

  • DROP_OLDEST:丢最老的,保最新的一批("滑窗")。

  • DROP_LATEST:丢最新的(更少见)。

  • capacity = 0(RENDEZVOUS):相当于无缓冲,交接点对点同步,不解耦。

  • UNLIMITED:不建议轻易用,可能吃内存。

和其它操作符的区别

  • conflate() ≈ buffer(1, DROP_OLDEST):永远只保"最新值",中间值丢弃,适合"只关心最新状态"的 UI。

  • collectLatest / mapLatest :来了新值会取消前一个下游处理(不是丢缓存,而是取消处理过程),适合"处理很重但只要最新"的场景(比如渲染)。

  • buffer 不会取消处理,只影响排队与丢弃策略

典型场景

  • 上游快、下游慢:日志、传感器、网络流 → buffer(64, SUSPEND)。

  • 只关心最新:搜索框联想、进度流 → conflate() 或 buffer(1, DROP_OLDEST)。

  • 限制突发 + 保持近实时:buffer(n, DROP_OLDEST)(比如 n=8/16)。

与flowOn的关系

  • flowOn(Dispatchers.IO) 自带一个缓冲边界(类似 buffer(Channel.BUFFERED)),使上下文切换处天然解耦。
  • 需要更深队列时,可在 flowOn 之后再加一个 buffer(k) 增大吞吐。
scss 复制代码
upstream
  .map { ioHeavy() }
  .flowOn(Dispatchers.IO) // 这里已形成一个缓冲边界
  .buffer(32)             // 再加一段队列
  .collect { ui() }

放置位置(很关键)

把 buffer() 放在慢操作之前,切开阻塞链条:

scss 复制代码
flow
  .map { fast() }
  .buffer(64)     // 切断:避免后面的慢步骤"顶住"前面
  .map { slow() }
  .collect { consume() }

与 SharedFlow/StateFlow

  • 它们自身有 replay/extraBufferCapacity/onBufferOverflow 。若再套 buffer(),会再多一层队列,只有在确实需要二级缓冲/隔离时才加。

小示例:对比分离前后

scss 复制代码
val f = flow {
  repeat(100) { i ->
    emit(i)             // 生产很快
    delay(10)
  }
}

// 无 buffer:下游慢会直接"顶住"上游
f.collect { delay(50); println(it) }

// 有 buffer:先排队,上游更流畅(满了再按策略处理)
f.buffer(32, onBufferOverflow = BufferOverflow.SUSPEND)
 .collect { delay(50); println(it) }

选择容量的小经验

  • 先用默认(≈64),观察队列积压内存(可埋点 length/max)。

  • 若突发明显且不想丢数据:升大一点,如 128/256。

  • 只要最新:conflate() 或 buffer(1, DROP_OLDEST)。

一句话总结

buffer 用"队列"把快上游与慢下游隔离;SUSPEND 保正确性,DROP_OLDEST 保实时性;把 buffer 放在慢步骤之前最有效。

相关推荐
Rain的Java大神之路2 小时前
短信接口被狂刷怎么处理
java·运维·后端·web安全·面试·架构·xss
官乐5 小时前
AI面试指南(多agent开发流程)
人工智能·面试·职场和发展
码匠许师傅5 小时前
【C++ 面试真题】27. 聊聊 C++ 的内存泄漏与内存布局
java·c++·面试
跟我一起学测试呀5 小时前
软件测试面试总结,备战金九银十
面试·职场和发展
swipe8 小时前
RabbitMQ 实战:AI Agent 里异步处理的标配方案(手把手 + 4 种交换机全解析)
后端·面试·langchain
早点睡97512 小时前
Python 上下文管理器深度剖析:从 with 语法糖到 CPython 底层协议
后端·面试
嵌入式阿蔡13 小时前
面试高频考点 01:volatile / 中断 / 堆栈 八股精讲
java·面试·职场和发展·嵌入式实时数据库
码匠许师傅13 小时前
# 【C++ 面试真题】28. 聊聊 C++ 的裸指针改造
java·c++·面试
小的~~14 小时前
面试被问分布式锁,我差点语塞…直到搞懂了Redis、ZooKeeper和etcd的“三国杀”
redis·分布式·面试
程序员爱钓鱼14 小时前
Go 编程实战:匿名函数 Anonymous Function——没有名字的函数与灵活回调
后端·面试·go