project reactor

project reactor (1)

注意,本文讲解一句官方文档 https://projectreactor.io/docs/core/release/reference/#flux;
同时对于其中过期的api也会进行适当扩展。

核心

flux

an Asynchronous Sequence of 0-N Items
一个表示 包含0-n个元素的 异步序列。

mono

an Asynchronous 0-1 Result
一个表示 包含1个元素或者没有元素的 异步结果。

创建并订阅flux和mono

创建

Flux<String> seq1 = Flux.just("foo", "bar", "foobar");

List<String> iterable = Arrays.asList("foo", "bar", "foobar");
Flux<String> seq2 = Flux.fromIterable(iterable);
Mono<String> noData = Mono.empty(); 

Mono<String> data = Mono.just("foo");
// 第一个参数是范围的开始,而第二个参数是要生成的itmes数。
Flux<Integer> numbersFromFiveToSeven = Flux.range(5, 3); 

订阅

subscribe 使用了java8的lambda函数表达式,若不熟悉java8函数表达式以及stream流操作api的童鞋,强烈建议学习一下!不光是为了下面的内容铺垫,它真的很厉害!
包含以下变种:

// 订阅并触发序列。
subscribe(); 
// 为每个产生的value做一些逻辑。
subscribe(Consumer<? super T> consumer); 
// 处理值,但也会对错误作出反应。
subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer); 
// 处理值和错误,但在序列成功完成后还要运行一些代码。
subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer,
          Runnable completeConsumer); 
// 处理值和错误并成功完成,但也可以使用此 subscribe 调用产生的Subscription 执行某些操作。
subscribe(Consumer<? super T> consumer,
          Consumer<? super Throwable> errorConsumer,
          Runnable completeConsumer,
          Consumer<? super Subscription> subscriptionConsumer); 

这些变体返回对subscribe 的引用,我们可以在不需要更多数据时使用该引用来取消订阅。 取消后,源应停止产生值并清除其创建的任何资源。

这种取消和清理行为在Reactor中由通用的Disposable接口表示。

subscribe 示例(五个变种)

1 简单操作

// 设置一个Flux,当添加一个订阅者之后,该Flux会产生三个值。
Flux<Integer> ints = Flux.range(1, 3); 
// 以最简单的方式订阅。
ints.subscribe(); 

2 消费操作

Flux<Integer> ints = Flux.range(1, 3); 
// 订阅者订阅并打印对应的value
ints.subscribe(System.out::println); 

3 2+error回调

Flux<Integer> ints = Flux.range(1, 4)
                .map(i -> {
                    if (i <= 3) {
                        return i;
                    }
                    throw new RuntimeException("Got to 4");
                });
ints.subscribe(
		System.out::println,
		error -> System.err.println("Error: " + error)
	);

输出:

1
2
3
Error: java.lang.RuntimeException: Got to 4

4 3+completion events 回调

Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(
        System.out::println,
        error -> System.err.println("Error " + error),
        // 添加 completion events 处理器
        () -> System.out.println("complete Done")
);

输出:

1
2
3
4
complete Done

注意:error 信号和complete 信号都是 终端事件(stream中的概念),并且彼此互斥。 为了使完成completion
events 处理器工作,因此示例代码没有触发error 信号。
complete 回调没有输入,由一对空括号表示:它与Runnable接口中的run方法匹配。

5 4+Subscription 操作

该变体需要您对Subscription进行操作(对它执行一个request(long)或cancel())。 否则,flux会挂起。

Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(System.out::println,
        error -> System.err.println("Error " + error),
        () -> System.out.println("Done"),
        // 当我们订阅时,我们会收到一个 Subscription。这里表示我们要从源中获取最多10个元素(实际上将发出4个元素并完成)。
        sub -> sub.request(10));

注意,新版3.5 的api已经弃用此变种,官方的解释是使用者经常会忘记 subsciption,因此如果确实需要的话官方建议使用 subscribeWith(Subscriber) 代替。
就像这样就不会忘记啦/捂脸,怎么会有种奇葩的感觉:

ints.subscribeWith(new Subscriber<Integer>() {
            @Override
            public void onNext(Integer integer) {
                System.out.println(integer);
            }
            @Override
            public void onError(Throwable throwable) {
                System.err.println("Error " + throwable);
            }
            @Override
            public void onComplete() {
                System.out.println("Done");
            }
            @Override
            public void onSubscribe(Subscription subscription) {
                subscription.request(10);
            }
        });
使用 返回的Disposable取消一个 subscribe()

以上那些基于lambda的 subscribe()变体都具有 Disposable 返回类型。因此在这种情况下,可以通过调用其dispose()方法来取消订阅。

对于flux和mono来说,cancel 是让信号源应停止产生 元素 的信号。 但是这个操作并不能保证立即执行:某些源可能以很快的速度产生元素,以至于在接收到取消指令之前已经完成了所有的生产动作。

Disposables类中提供了一些有关Disposable的实用程序。其中,Disposables.swap()创建一个Disposable包装器,使我们可以原子地取消和替换一个具体的Disposable。

另一个有趣的实用程序是Disposables.composite(…)。 通过此组合,可以收集多个Disposable(例如,与服务调用关联的多个进行中的请求),并在以后一次性处理所有这些请求。 调用组合的dispose()方法后,任何尝试添加另一个Disposable都会立即被dispose。

lambda的替代方案 BaseSubscriber

还有一种附加的订阅方法,该方法更通用并且采用成熟的订阅服务器,而不是从一个lambda表达式中生成。

BaseSubscriber(或其子类)的实例是一次性的,这意味着,如果BaseSubscriber订阅了第二个发布者,则取消其对第一个发布者的订阅。 这是因为使用实例两次将违反“反应式流”规则,即不得并行调用订阅服务器的onNext方法。

示例:

package cn.lucky.reactor.demo1;

import org.reactivestreams.Subscription;
import reactor.core.publisher.BaseSubscriber;
/**
默认情况下,BaseSubscriber会触发一个无限制的请求,其行为与Subscribe()完全相同。
但是,当需要自定义请求量时,扩展BaseSubscriber会更加有用。
**/
public class SampleSubscriber<T> extends BaseSubscriber<T> {

	@Override
	public void hookOnSubscribe(Subscription subscription) {
		System.out.println("Subscribed");
		// 自定义请求量  一次一个请求
		request(1);
	}

	@Override
	public void hookOnNext(T value) {
		System.out.println(value);
		// 自定义请求量  一次一个请求
		request(1);
	}
}

测试:

SampleSubscriber<Integer> ss = new SampleSubscriber<Integer>();
Flux<Integer> ints = Flux.range(1, 4);
ints.subscribe(ss);

输出:

Subscribed
1
2
3
4

BaseSubscriber还提供了requestUnbounded()方法以切换到无界模式(等效于request(Long.MAX_VALUE)),当然还提供了cancel()方法。

另外和lambda中的变种类似,它还具有其他钩子:hookOnComplete,hookOnError,hookOnCancel和hookFinally(在序列终止时始终调用,终止类型作为SignalType参数传递)。对于这些钩子方法童鞋可自行实现,示例中的 SampleSubscriber 是执行绑定请求的订阅服务器的绝对最小实现。

Backpressure 和重塑 requests

在Reactor中实现Backpressure 时,通过向上游 operator发送 request ,将consumer pressure 传播回source 。 当前请求的总和有时被称为当前“需求”或“待处理请求”。 需求的上限为Long.MAX_VALUE,表示无限制的请求(意味着“尽快生成”,这样基本上是禁用Backpressure )。

第一个请求在订阅时来自 final 订阅者,但是下面这些最直接的订阅方式立即触发了Long.MAX_VALUE的无限制请求:

  • subscrbe()及其大多数基于lambda的变体(具有Consumer 的变量除外)
  • block(), blockFirst() 和 blockLast()
  • 通过toIterable() 或者 toStream() 进行遍历

定制原始请求的最简单方法是使用BaseSubscriber进行subscribe,并覆盖hookOnSubscribe方法,如以下示例所示:

Flux.range(1, 10)
                .doOnRequest(r -> System.out.println("第 " + r +" 个请求"))
                .subscribe(new BaseSubscriber<Integer>() {

                    @Override
                    public void hookOnSubscribe(Subscription subscription) {
                        request(1);
                    }

                    @Override
                    public void hookOnNext(Integer integer) {
                        System.out.println("收到 " + integer +" 之后cancel ");
                        cancel();
                    }
                });

输出:

1 个请求
收到 1 之后cancel

在处理请求时,必须小心以产生足够的需求来推进序列,否则您的Flux可能会“卡住”。 这就是为什么BaseSubscriber在hookOnSubscribe中默认为无界请求的原因。 覆盖此挂钩时,通常应至少调用一次请求。

改变下游需求的Operators
要记住的一件事是,上游链中的每个Operator 都可以调整在订阅级别表达的需求。 一个教科书式的例子是 buffer(N)运算符:如果它接收到request(2),则将其解释为对两个完整缓冲区的需求。 结果,由于缓冲区需要将N个元素视为已满,因此缓冲区运算符会将请求重塑为2 xN。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值