背压问题---Flow

228 阅读3分钟

背压问题---Rxjava 中,讲到,背压问题产生的原因以及Rxjava的相关处理办法,那在Kotlin时代,有木有更好的替代方案呢?

基于协程的Flow

Flow 是基于协程使用的,它不需要一些巧妙设计的解决方案来明确处理背压,在 Flow 中,不同于一些传统的响应式框架,它的背压管理是使用 Kotlin 挂起函数 suspend 实现的, 它里面所有的函数方法都是使用 suspend 修饰符标记,这个修饰符就是为了暂停调度者的执行不阻塞线程。因此, Flow<T> 在同一个协程中发射和收集时,如果收集器跟不上数据流,它可以简单地暂停元素的发射,直到它准备好接收更多。

Flow背压策略

主要有如下三种,可以对比 Rxjava Flowable 中的5种有异曲同工的作用。

image.png

  • SUSPENDbuffer() 方法中默认):会将当前协程挂起,直到缓冲区中的数据被消费了
  • DROP_OLDEST :它会丢弃最老的数据
  • DROP_LATEST : 它会丢弃最新的数据

参考源码:

onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND): Flow<T> {})

buffer() 方法中可定义 onBufferOverflow参数,于是就有了:

1、SUSPEND 策略,默认方式,会挂起当前函数,直到执行完毕

对应的 buffer() 方法

image.png

2、DROP_OLDEST 策略

image.png

也就是:

image.png

在一些些情况下,由于生产消费速率不匹配的问题, conflate 将做取舍,丢弃掉旧数据,只有在收集器空闲之前发出的最后一个元素才被收集, 这样做的好处就是执行时间缩短,不受积压影响,但相应的是数据丢失。

3、DROP_LATEST 策略

按源码中的解释是,collectLatest 作用在于当原始流发出一个新的值的时候,前一个值的处理将被取消,也就是不会被接收, 和 conflate 的区别在于它不会用新的数据覆盖,而是每一个都会被处理,只不过如果前一个还没被处理完后一个就来了的话,处理前一个数据的逻辑就会被取消。

image.png

也就是:

image.png

策略方案对比

如果每一个事件都很重要, 可以用:buffer() ,即 SUSPEND 策略; 如果事件可以覆盖,只需获取最新的, 例如:数据库刷新,可以用:collectLatest,即 DROP_LATEST 策略;(ps:我们在跑步项目中,使用的数据库记录数据,也有可能会频繁更新,可以考虑这样的策略) 如果事件事件不重要,只关心发送次数,可以用:conflate(),即 DROP_OLDEST 策略

FlowRxjava 的简单比较

RxJava FlowableKotlin Flow 都支持背压,但仍然存在差异。 主要是 RxJava 基于内置的背压支持,它从下到上工作(下游能够在需要更多值时告诉上游),而 Kotlin Flow 背压主要基于 suspend 挂起函数 Rxjava 会将它进行阻塞,而 Flow 则将它挂起/暂停。

如果项目中有使用Rxjava 就可以不用flow,Rxjava 的代码量是相当多的; 如果项目中使用了协程,就可以不用Rxjava,协程相对来说更轻量级,适合新项目,初学者;