RxJava2 系列-2:背压和 Flowable

Android Rxjava2 :最简单&全面背压讲解 (Flowable) 1.前言 阅读本文需要对Rxjava了解,如果还没有了解或者使用过Rxjava的兄die们,可以观看我另外一篇 Android Rxjava:不一样的诠释进行学习。 Rxjava背压:被观察者发送事件的速度大于观察者接收事件的速度时,观察者内会创建一个无限制大少的缓冲池存储未接收的事件,因此当存储的事件越来越多时就会导致OOM的出现。(注:当subscribeOn与observeOn不为同一个... 阅读详情

背压(Back Pressure)的概念最初并不是在响应式编程中提出的,它最初用在流体力学中,指的是后端的压力,
通常用于描述系统排出的流体在出口处或二次侧受到的与流动方向相反的压力。

在响应式编程中,我们可以将产生信息的部分叫做上游或者叫生产者,处理产生的信息的部分叫做下游或者消费者。
试想如果在异步的环境中,生产者的生产速度大于消费者的消费速度的时候,明显会出现生产过剩的情景,这时候就需要消费者对多余的数据进行缓存,
但如果生产的信息数量过多,以至于超出缓存大小,就会出现缓存溢出,甚至可能造成内存耗尽。

我们可以制定一个数据丢失的规则,来丢失那些“可以丢失的数据”,以减轻缓存的压力。
在之前我们介绍了一些方法,比如throttleXXX、debounce、sample等,都是用来解决在生产速度过快的情况下的数据过滤的,它们指定了数据取舍的规则。
而在Flowable,我们可以通过onBackpressureXXX一系列的方法来制定当数据生产过快情况下的数据取舍的规则,

我们可以把这种处理方式理解成背压,所谓背压,在Rx中就是通过一种下游用来控制上游事件发射频率的机制(就像流体在出口受到了阻力一样)。
所以,如何理解背压呢?笔者认为,在力学中它是一种现象,在Rx中它是一种机制。

在这篇文章中,我们会先介绍背压的相关内容,然后我们再介绍一下onBackpressureXXX系列的方法。

关于RxJava2的基础使用和方法梳理可以参考:RxJava2 系列 (1):一篇的比较全面的 RxJava2 方法总结

说明:以下文章部分翻译自RxJava官方文档Backpressure (2.0)。

1、背压机制

如果将生产和消费整体看作一个管道,生成看作上游,消费看作下游;
那么当异步的应用场景下,当生产者生产过快而消费者消费很慢的时候,可以通过背压来告知上游减慢生成的速度。

通常在进行异步的操作的时候会通过缓存来存储发射出的数据。在早期的RxJava中,这些缓存是无界的。
这意味着当需要缓存的数据非常多的时候,它们可能会占用非常多的存储空间,并有可能因为虚拟机不断GC而导致程序执行过慢,甚至直接抛出OOM。
在最新的RxJava中,大多数的异步操作内部都存在一个有界的缓存,当超出这个缓存的时候就会抛出MissingBackpressureException异常并结束整个序列。

然而,某些情况下的表现会有所不同,它们不会抛出MissingBackpressureException异常。比如下面的range操作:

private static void compute(int i) throws InterruptedException {
    Thread.sleep(500);
    System.out.println("computing : " + i);
}

private static void testFlowable() throws InterruptedException {
    Flowable.range(1, MAX_LENGTH).observeOn(Schedulers.computation()).subscribe(FlowableTest::compute);

    Thread.sleep(500 * MAX_LENGTH);
}

在这段代码中我们生成一段整数,然后每隔500毫秒执行依次计算操作。从输出的结果来看,在程序的实际执行过程中,数据的发射是串行的。
也就是发射完一个数据之后进入compute进行计算,等待500毫秒之后才发射下一个。
因此,在程序的执行过程中没有抛出异常,也没有过多的内存消耗。

而下面的这段代码就会在程序运行的时候立刻抛出MissingBackpressureException异常:

PublishProcessor<Integer> source = PublishProcessor.create();
source.observeOn(Schedulers.computation()).subscribe(v -> compute(v), Throwable::printStackTrace);
for (int i = 0; i < 1_000_000; i++) source.onNext(i);
Thread.sleep(10_000);

这是因为PublishProcessor底层会调用PublishSubscription,而后者实现了AtomicLong,它会通过判断引用的long是否为0来抛出异常,这个long型整数会在调用PublishSubscription.request()的时候被改写。前面的一个例子的原理就是当每次调用了观察者的onNext之后会调用PublishSubscription.request()来请求数据,这样相当于消费者会在消费完事件之后向生产者请求,因此整个序列的执行看上去是串行的,从而不会抛出异常。

2、onBackpressureXXX

大多数开发者在遇到MissingBackpressureException通常是因为使用observeOn方法监听了非背压的PublishProcessor, timer(), interval()或者自定义的create()。我们有以下几种方式来解决这个问题:

2.1 增加缓存大小

observeOn方法的默认缓存大小是16,当生产的速率过快的时候,那么可能很快会超出该缓存大小,从而导致缓存溢出。
一种简单的解决办法是通过提升该缓存的大小来防止缓存溢出,我们可以使用observeOn的重载方法来设置缓存的大小。比如:

PublishProcessor<Integer> source = PublishProcessor.create();
source.observeOn(Schedulers.computation(), 1024 * 1024)
      .subscribe(e -> { }, Throwable::printStackTrace);

但是这种解决方案只能解决暂时的问题,当生产的速率过快的时候还是有可能造成缓存溢出,所以这不是根本的解决办法。

2.2 通过丢弃和过滤来减轻缓存压力

我们可以根据自己的应用的场景和数据的重要性,选择使用一些方法来过滤和丢弃数据。
比如,丢弃的方式可以选择throttleFirst, throttleLast, throttleWithTimeout等,还可以使用按照时间采样的方式来减少接受的数据。

PublishProcessor<Integer> source = PublishProcessor.create();
source.sample(1, TimeUnit.MILLISECONDS)
      .observeOn(Schedulers.computation(), 1024)
      .subscribe(v -> compute(v), Throwable::printStackTrace);

但是,这种方式仅仅用来减少下游接收的数据,当缓存的数据不断增加的时候还是有可能导致缓存溢出,所以,这也不是一种根本的解决办法。

2.3 onBackpressureBuffer()

这种无参的方法会使用一个无界的缓存,只要虚拟机没有抛出OOM异常,它就会把所有的数据缓存起来。

 Flowable.range(1, 1_000_000)
           .onBackpressureBuffer()
           .observeOn(Schedulers.computation(), 8)
           .subscribe(e -> { }, Throwable::printStackTrace);

上面的例子即使使用了很小的缓存也不会有异常抛出,因为onBackpressureBuffer会将发射的所有数据缓存起来,只会将一小部分的数据传递给observeOn。

这种处理方式实际上是不存在背压的,因为onBackpressureBuffer缓存了所有的数据,我们可以使用该方法的4个重载方法来对背压进行个性化设置。

2.4 onBackpressureBuffer(int capacity)

这个方法使用一个有界的缓存,当达到了缓存大小的时候会抛出一个BufferOverflowError错误。
通过这种方法可以增加默认的缓存大小,但是通过observeOn方法一样可以指定缓存的大小,因此,这个方法的应用变得越来越少。

2.5 onBackpressureBuffer(int capacity, Action onOverflow)

这方法除了可以指定一个有界的缓存还提供了一个,当缓存溢出的时候还会回调指定的Action。
但是这种回调的用途比较有限,因为它除了提供当前回调的栈信息以外提供不了任何有用的信息。

2.6 onBackpressureBuffer(int capacity, Action onOverflow, BackpressureOverflowStrategy strategy)

这个重载方法相对比较实用一些,它除了上面的那些功能之外,还指定了当缓存到达指定的缓存时的行为。
这里的BackpressureOverflowStrategy顾名思义是一个策略,它是一个枚举类型,预定义了三种枚举值,最终会在FlowableOnBackpressureBufferStrategy中根据指定的枚举类型选择不同的实现策略,因此,我们可以使用它来指定缓存溢出时候的行为。

下面是该枚举类型的三个值及其含义:

  1. ERROR:当缓存溢出的时候会抛出一个异常;
  2. DROP_OLDEST:当缓存发生溢出的时候,会丢弃最老的值,并将新的值插入到缓存中;
  3. DROP_LATEST:当缓存发生溢出的时候,最新的值会被忽略,只有比较老的值会被传递给下游使用;

需要注意的地方是,后面的两种策略会造成下游获取到的值是不连续的,因为有一部分值会因为缓存不够被丢弃,但是它们不会抛出BufferOverflowException。

2.7 onBackpressureDrop()

这个方法会在数据达到缓存大小的时候丢弃最新的数据。可以将其看成是onBackpressureBuffer+0 capacity+DROP_LATEST的组合。

这个方法特别适用于那种可以忽略从源中发射出值的那种场景,比如GPS定位问题,定位数据会不断发射出来,即使丢失当前数据,等会儿一样能拿到最新的数据。

component.mouseMoves()
    .onBackpressureDrop()
    .observeOn(Schedulers.computation(), 1)
    .subscribe(event -> compute(event.x, event.y));

该方法还存在一个重载方法onBackpressureDrop(Consumer<? super T> onDrop),它允许我们传入一个接口来指定当某个数据被丢失时的行为。

2.8 onBackpressureLatest()

对应于onBackpressureDrop()的,还有onBackpressureLatest()方法,该方法只会保留最新的数据并会覆盖较老、没有分发的数据。
我们可以将其看成是onBackpressureBuffer+1 capacity+DROP_OLDEST的组合。

与onBackpressureDrop()不同的地方在于,当下游消费过慢的时候,这种方式总会存在一个缓存的值。
这种特别适用于那种数据的生产非常频繁,但是只有最新的数据会被消费的那种情形。比如,当用户点击了屏幕,那么我们倾向于只处理最新按下的位置的事件。

component.mouseClicks()
    .onBackpressureLatest()
    .observeOn(Schedulers.computation())
    .subscribe(event -> compute(event.x, event.y), Throwable::printStackTrace);

所以,总结一下:

  1. onBackpressureDrop():不会缓存任何数据,专注于当下,新来的数据来不及处理就丢掉,以后会有更好的;
  2. onBackpressureLatest():会缓存一个数据,当正在执行某个任务的时候有新的数据过来,会把它缓存起来,如果又有新的数据过来,那就把之前的替换掉,缓存里面的总是最新的。

3、总结

以上就是背压机制的一些内容,以及我们介绍了Flowable中的几个背压相关的方法。
实际上,RxJava的官方文档也有说明——Flowable适用于数据量比较大的情景,因为它的一些创建方法本身就使用了背压机制。
这部分方法我们就不再一一进行说明,因为,它们的方法签名和Observable基本一致,只是多了一层背压机制。

比较匆匆地整理完了背压的内容,但是我想这块还会有更加丰富的内容值得我们去发现和探索。

以上。

基于Pycatia的智能孔特征识别技术:无参模型圆心自动化提取方案 阅读详情

相关推荐

DSSAT模型实战:从基础配置到气候情景模拟

本文详细介绍了DSSAT模型在农业模拟中的应用,从基础配置到气候情景模拟的全流程实战指南。通过土壤-植物-大气连续体的动态关系建模,DSSAT能精准预测作物产量,误差率低至8%。文章涵盖气象数据处理、土壤参数配置、管理措施调控等核心技巧,并提供了Python脚本和可视化方法,助力农业科研与决策。

weixin_29264201的博客 1010

AI伦理冲突:传统智慧与算法逻辑的碰撞与融合

人工智能伦理是当前AI发展中的核心议题,涉及算法决策与人类价值观的深度协调。从技术原理看,机器学习基于数据驱动和统计规律,而传统智慧体系则强调直觉认知和价值理性。这种根本差异在医疗、金融等应用场景中表现为算法透明性、价值对齐等实际问题。通过构建混合认知架构、嵌入伦理约束项等技术手段,可以实现AI系统的伦理增强。典型如医疗AI的安宁疗护决策、招聘系统的偏见消除等案例,展示了伦理算法设计的工程实践价值。

weixin_33721344的博客 582

树莓派使用 OLED 屏显示图片及文字

树莓派默认是不带显示屏的,如果想要查看系统的一些信息,需要使用电脑登录到树莓派,或者通过 HDMI 连接外接显示器查看。这样做总是有点麻烦,我们可以通过外接一个 OLED 屏来显示一些关键参数或者图片。本文将详细介绍操作方法。 OLED 模组介绍 OLED 屏主要有两种:128×32 和 128×64 ,主要颜色是白、黄、蓝。OLED 屏的特点是功耗低,价格便宜,使用 I2C 接口与主机连接。本文所使用的是 128×64 尺寸的屏幕,外形如下: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直

码到成功 3742

RxJava 并发之数据流发射太快如何办(背压(Backpressure))

RxJava系列教程:1. RxJava使用介绍 【视频教程】 2. RxJava操作符   • Creating Observables(Observable的创建操作符) 【视频教程】   • Transforming Observables(Observable的转换操作符) 【视频教程】   • Filtering Observables(Observable的过滤操

Dream It Possible 1万+

从交换机安全配置,看常见局域网攻击

前言 构建零信任网络,自然离不开网络准入(NAC),这就涉及到交换机的一些安全测试,于是有了此文《从交换机安全配置看常见局域网攻击》。 交换机安全配置 如本文标题所说从交换机安全配置看常见的局域网攻击,那么下面提到的各种攻击方式,自然是建立在交换机安全配置之上来说的。我们通过h3c官方提供的《H3C 交换机安全加固手册》和《安全配置指导》,从这两份手册可以看出交换机支持的安全配置非常多,基本已经覆盖了各种我们熟知局域网攻击。下面就挑几个典型的攻击方式进行演示。 arp欺骗 【一>所有资源获取&lt

kali_Ma的博客 1万+

IDEA 2023.2.3全平台安装指南:Mac/Windows/Linux一键激活(附最新补丁获取)

本文提供IDEA 2023.2.3在Mac、Windows和Linux三大平台的一键安装与激活指南,包含最新补丁获取方法。详细讲解各系统安装流程、性能调优设置及高效开发技巧,帮助开发者快速配置并提升工作效率。

weixin_28832121的博客 391

Rxjava--背压(Backpressure)

前言:Rxjava是通过观察者模式设计的异步任务框架,他的有点在于简洁性,不是代码的简洁性,而是逻辑的简洁性,随着项目的中异步任务逻辑越来越复杂,Rxjava可以一直保持代码逻辑的简洁,不会像handler,ThreadHandler这些在几次线程间切换以后可能都已经晕头转向了.Rxjava提供了多种类的操作符,比如ObserverOn可以为每次业务的处理的处理制定线程,flatmap这种操作符...

kingwjh的专栏 800

RK3576+IMX415 MIPI摄像头零拷贝显示系统

MIPI CSI-2是嵌入式视觉系统中主流的高速图像传感器接口标准,其核心价值在于低延迟、高带宽与硬件级DMA直通能力。基于V4L2子系统与DRM/KMS驱动栈,可构建从传感器RAW数据到帧缓冲区的零拷贝通路,显著降低CPU负载并提升实时性。该技术广泛应用于工业检测、边缘AI推理前数据接入及多路同步采集等场景。本文以Rockchip RK3576 SoC和索尼IMX415传感器为典型组合,详解MIPI物理层适配、内核驱动集成、GStreamer零拷贝管道搭建及systemd服务化部署,覆盖从硬件信号完整性到

weixin_36047538的博客 216

51、基于注解方式开发Spring WebFlux,实现生成背压数据,就是实现一直向客户端发送消息

基于注解方式开发Spring WebFlux,实现生成背压数据,就是实现一直向客户端发送消息

Java 领域技术分享 1333

YOLOv5模型转换陷阱:从PT到RKNN的置信度异常与乱框问题深度解析

本文深度解析了YOLOv5模型从PT到RKNN转换过程中的置信度异常与检测框错乱问题,揭示了NPU硬件特性与模型结构不匹配的根本原因。通过详细的解决方案和实操步骤,包括模型修改、转换流程配置和验证技巧,帮助开发者有效解决这一常见部署难题,提升模型在瑞芯微芯片上的推理性能。

weixin_29062255的博客 902

避开RK3588开发板SDIO配置的坑:O9201SB WiFi模组接入实战指南

本文为嵌入式开发者提供了在RK3588开发板上,利用TF卡槽接入O9201SB WiFi6模组的实战指南。文章深度剖析了SDIO配置中常见的电源管理序列冲突问题,并给出了从设备树节点修改、内核编译到系统验证的完整解决方案,帮助开发者避开配置陷阱,成功实现高性能无线连接。

sprite的博客 954

Android RxJava学习记录(三)

(八)https://www.jianshu.com/p/a75ecf461e02 在上篇记录中讲到,Flowable默认有一个大小为128的“水缸”。一旦超过128就会抛出MissingBackpressureException异常。 实际上,Flowable可以使用更大的“水缸”,只需指定 BackpressureStrategy.BUFFER Flowable.create(new ...

KWMax 211

YOLO11多个点组合创新:GC10-DET缺陷检测 | DCNv4结合SPPF+11Detect创新性结合

DCNv4结合SPPF+11Detect创新性结合,原始mAP50为0.633 提升至0.651

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1286

flowable画图教程_给初学者的RxJava2.0教程(七): Flowable

作者博客http://www.jianshu.com/u/c50b715ccaeb前言上一节里我们学习了只使用Observable如何去解决上下游流速不均衡的问题,之所以学习这个是因为Observable还是有很多它使用的场景,有些朋友自从听说了Flowable之后就觉得得Flowable解决任何问题,甚至有抛弃Observable这种想法,这是万万不可的,它们都有各自的优势和不足。在这一节里我们...

weixin_36272622的博客 305

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

给初学者的RxJava2.0教程(八)

给初学者的RxJava2.0教程(八) 作者 Season_zlc 关注 2016.12.22 16:30 字数 1904 阅读 8921评论 46喜欢 119赞赏 4 Outline [TOC] 前言 在上一节中, 我们学习了FLowable的一些基本知识, 同时也挖了许多坑, 这一节就让我们来填坑吧. 正题 在上一节中最后我们有个例子, 当上游一次性发送1

گ 我就是我我只为自己代言 گ 421

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【Android】Rxjava2 Flowable详解与背压那些事

1.Rxjava1中的背压 Rxjava2中有这么一个被观察者Flowable,同样作为被观察者,它和Observable有什么区别呢,在Rxjava2中,Observable不再支持背压,而新增的Flowable支持背压,何为背压,就是上游发送事件的速度大于下游处理事件的速度所产生的现象。 我们来看个例子,先把rxjava切换到rxjava1.0: implementation 'io.reac...

风吹起如花般细碎流年 2588

RxJava2背压-Flowable

转载自冯丰枫的博客 Flowable介绍 既然在函数响应式编程中会产生背压(backpressure)问题,那么在函数响应式编程中就应该有解决方案。 Rxjava2相对于Rxjava1最大的更新就是把对背压问题的处理逻辑从Observable中抽取出来产生了新的可观察对象Flowable。 在Rxjava2中,Flowable可以看做是为了解决背压问题,在Observable的基础上优化后的产物,...

tinysakura的博客 1614

rxjava背压

没错,没给你闹着玩,他真的又抛出大家喜欢的异常了,没毛病。不知道,大家看完了RxJava1.0的背压,对它有什么看法?下面是我对RxJava1.0背压的一些理解。

2401_84121998的博客 916

RxJava(七)背压Flowable

RxJava背压Flowable 1.背压:在RxJava的异步场景中,被观察者发送数据太快,以至于操作符和订阅者来不及处理数据,造成buffer溢出。 2.Flowable:RxJava中专门用来支持背压,默认队列大小128,所有操作符强制支持背压。 3.背压策略: public enum BackpressureStrategy{ MISSING, //通过creat...

android开发笔记 2046

Android-Rxjava-:最简单&全面背压讲解-(Flowable)(1)

上图可以很清楚看出二者的区别,其实Flowable出来以上的区别之外,它其他所有使用与Observable完全一样。Flowable的create例子@Overridej

2401_85729264的博客 925

RxJava2 背压

产生条件:解决背压问题的方法: 上面的代码执行的是buffer的背压策略。 在Android中运行以上代码,会立即引起App Crash,引起以下Exception: 因为Flowable的默认队列是128,所以讲上述代码的129改成128,程序就可以正常运行了。 上述代码不会导致崩溃,但会引起ANR。 在Android中运行这段代码,不会引起Crash,但只会打印出0~127,第128则被丢弃,因为Flowable的内部队列已经满了。 执行结果: 3. 其他 示例:...

1176

RxJava——Flowable与背压

策略数据丢失内存风险行为特点BUFFER​否高​无限缓存,等待消费DROP​是​低直接丢弃来不及处理的数据LATEST​是​低丢弃旧数据,只保留最新一个ERROR​是​低直接抛异常,流终止MISSING​视后续操作而定高​不处理,交由下游对数据完整性要求极高,且流量可控 -> 考虑 BUFFER(但需注意缓冲区大小限制)。允许丢弃中间状态,只关心最新状态​ -> LATEST(如UI刷新)。允许丢弃数据,且不要求最新 -> DROP(如非关键指标采样)。希望。

吴声子夜歌的博客 737

RxJava背压

RxJava背压主要用来解决异步订阅关系中,被观察者发送事件的速度与观察者接收事件的速度不匹配的问题。大多数情况下,由于被观察者发送事件速度太快,而观察者来不及响应处理所有事件,从而导致事件丢失、OOM等异常。 RxJava 2.0中对背压的支持 主要是通过Flowable类来实现的,Flowable其实就是被观察者(Observable)的一种新实现,用来解决RxJava 1.0中无法解决的背压...

川峰的博客 1506

rxjava背压,Android开发教程入门

找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。多练。(视频优势是互动感强,容易集中注意力)你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!取。

2401_83817603的博客 1049
上一篇: RxJava2 系列-1:一篇的比较全面的 RxJava2 方法总结
下一篇: RxJava2 系列-3:Subject 及其几种应用方式
开发者如是说
博客等级 码龄10年 67粉丝 163原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值