RxJava学习 - 12. Flowables and Backpressure

本文详细介绍了RxJava中的Flowables和背压控制。Flowable是Observable的背压版本,允许上游以下游可处理的速度发射元素,防止内存溢出。文章通过示例解释了背压的必要性,并展示了如何创建Flowable,使用BackpressureStrategy以及onBackpressureXXX()操作符进行背压管理。还探讨了何时使用Flowables和背压策略,以及如何在Flowable生成器中实现背压。
摘要由CSDN通过智能技术生成

当生产更快的时候,最好的办法是让源变慢,以下游能接受的速度发射。这叫backpressure或者流量控制,可以使用Flowable代替Observable。

Understanding backpressure

Observables的本质是基于push的。元素同步地,一次一个地到达Observer。默认不使用并发。
比如,下面的Observable发射1-999999999。它把每个整数映射到MyItem实例——它只有一个属性。让我们放慢速度,Observer每50毫秒处理一个元素。可以看出,如果下游处理速度慢,上游也同步地变慢。因为所有工作都是一个线程完成的:

import io.reactivex.Observable;
import java.util.concurrent.TimeUnit;

public class Launcher {
   
    public static void main(String[] args) {
   
        Observable.range(1, 999_999_999)
                .map(MyItem::new)
                .subscribe(myItem -> {
   
                    sleep(50);
                    System.out.println("Received MyItem " + myItem.id);
                });
    }

    public static void sleep(int millis) {
   
        try {
   
            Thread.sleep(millis);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    
    static final class MyItem {
   
        final int id;
        MyItem(int id) {
   
            this.id = id;
            System.out.println("Constructing MyItem " + id);
        }
    }    
}

An example that needs backpressure

当Observable链中有了并发操作,操作变成了异步的。这意味着在某个时刻,Observable链上的多个部分可以都在处理emissions,生产者可以超过消费者,因为他们在不同的线程上工作。不再严格地一次一个地把emission交给下游。因此,源可能在前一个emission还没到达Observer就发送下一个。
前面的例子,我们在subscribe()之前,加上observeOn(Shedulers.io()):

import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import java.util.concurrent.TimeUnit;

public class Launcher {
   
    public static void main(String[] args) {
   
        Observable.range(1, 999_999_999)
                .map(MyItem::new)
                .observeOn(Schedulers.io())
                .subscribe(myItem -> {
   
                    sleep(50);
                    System.out.println("Received MyItem " + myItem.id);
                });
        sleep(Long.MAX_VALUE);
    }

    public static void sleep(int millis) {
   
        try {
   
            Thread.sleep(millis);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    
    static final class MyItem {
   
        final int id;
        MyItem(int id) {
   
            this.id = id;
            System.out.println("Constructing MyItem " + id);
        }
    }    
}

我的机器上,当增加了1001902个MyItem,Observer才处理了38个。来不及处理的被放到observeOn()的队列,会导致很多问题,甚至OutOfMemoryError异常。

Introducing the Flowable

Flowable是Observable的背压变种,告诉源以下游的节奏发射。
下面的代码,使用了Flowable.range(),整个链就使用Flowables工作了:

import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import java.util.concurrent.TimeUnit;

public class Launcher {
   
    public static void main(String[] args) {
   
        Flowable.range(1, 999_999_999)
                .map(MyItem::new)
                .observeOn(Schedulers.io())
                .subscribe(myItem -> {
   
                    sleep(50);
                    System.out.println("Received MyItem " + myItem.id);
                });
        sleep(Long.MAX_VALUE);
    }

    public static void sleep(int millis) {
   
        try {
   
            Thread.sleep(millis);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }
    
    static final class MyItem {
   
        final int id;
        MyItem(int id) {
   
            this.id = id;
            System.out.println("Constructing MyItem " + id);
        }
    }    
}

输出是

Constructing MyItem 1
Constructing MyItem 2
Constructing MyItem 3
......
Constructing MyItem 127
Constructing MyItem 128
Received MyItem 1
Received MyItem 2
Received MyItem 3
......
Received MyItem 95
Received MyItem 96
Constructing MyItem 129
Constructing MyItem 130
Constructing MyItem 131...
Constructing MyItem 223
Constructing MyItem 224
Received MyItem 97
Received MyItem 98
Received MyItem 99
......

源开始发射了128个emissions,然后,Flowable链一次处理了96个emissions的流。就像整个Flowable链努力保持它的管道内不超过96个元素一样。
Flowable.range()为什么发射了128个emissions,为什么observeOn()在请求另一个96个元素前,向下游发射了96个元素,而留下32个未处理的元素?emissions的最初批量会大一些,所以,如果有空闲时间,有些工作会排队。如果Flowable以请求96个元素开始,接着每次发射96个元素,会有等待下一个96个元素的时刻。因此,维护一个额外的32个长度的rolling cache,让空闲时刻有事情做。

When to use Flowables and backpressure

使用Flowable,减少了内存的使用,防止OutOfMemoryError异常。有些源不支持背压,如果使用背压,会导致MissingBackpressureException。
使用Flowable,会损失一部分性能。

如果Observable生存期内,发射的元素超与1000个,或者并不频繁,可以使用Observable。
如果你的操作是严格同步的,或者有限地使用了并发,可以使用Observable。包括在Observable链的开始,简单地使用subscribeOn(),因为这个处理还是使用单线程,元素被同步发给下游。当你在不同的线程上开始zipping和combining不同的流,或者使用了observeOn()、interval()和delay(),程序不再同步,应该使用Flowable。

Understanding the Flowable and Subscriber

Flowable.interval(),以固定的时间间隔发射。它是背压的吗?它的每个emission都是时间敏感的。如果我们放慢Flowable.interval(),
我们的emissions将不再反映时间间隔。因此,当下游请求背压时,Flowable.interval()会抛MissingBackpressureException:

import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import java.util.concurrent.TimeUnit;

public class Launcher {
   
    public static void main(String[] args) {
   
        Flowable.interval(1, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .map(i -> intenseCalculation(i))
                .subscribe(System.out::println, Throwable::printStackTrace);
        sleep(Long.MAX_VALUE);
    }

    public static void sleep(int millis) {
   
        t
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值