一 流的订阅
// 重载方法1
// 订阅流的最简单方法,忽略所有信号。通常用于触发具有副作用的流处理。
subscribe();
// 重载方法2
// 对每个值(onNext 信号)调用 dataConsumer,不处理 onError 和 onComplete信号。
subscribe(Consumer<T> dataConsumer);
// 重载方法3
// 与重载方法2相同,处理 onError 信号,忽略 onComplete 信号。
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer);
// 重载方法4
// 与重载方法3相同,处理 onComplete 信号。
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer,
Runnable completeConsumer);
// 重载方法5
// 消费响应式流中的所有元素,包括错误处理和完成信号。重要的是,这种重载方法能通过请求足够数量的数据来控制订阅,当然,请求数量仍然可以是 Long.MAX_VALUE。
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer,
RUnnable completeConsumer, Consumer<Subscription>
subscriptionConsumer);
// 重载方法6
// 订阅序列的最通用方式。在这里,可以为Subscriber的实现提供所需的行为。
subscribe(Subscriber<T> subscriber);
public void testSubscribe(){
Flux.just(1,0,3).subscribe(item -> {
System.out.println("item:" + 10/item);
},err -> { //异常完成触发
System.out.println(err);
},() -> { //正常完成触发
System.out.println("最终完成!");
});
Flux.range(1, 100)
.subscribe(
data -> System.out.println("onNext:" + data),
ex -> System.err.println("异常信息:" + ex.getMessage()),
() -> System.out.println("onComplete"),
subscription -> {
// 订阅响应式流5个元素
subscription.request(5);
// 取消订阅
subscription.cancel();
}
);
// 自定义订阅者
Flux.just(1,0,3).subscribe(new BaseSubscriber<Integer>() {
@Override
protected void hookOnSubscribe(Subscription subscription) {
System.out.println("数据到达,准备处理");
request(1);
}
@Override
protected void hookOnNext(Integer value) {
System.out.println("数据+"+10/value);
// if(value == 0) cancel();
request(1);
}
@Override
protected void hookOnComplete() {
System.out.println("正常完成");
}
@Override
protected void hookOnError(Throwable throwable) {
System.out.println(throwable);
System.out.println("异常完成");
cancel();
}
@Override
protected void hookOnCancel() {
System.out.println("取消数据获取");
}
@Override
protected void hookFinally(SignalType type) {
System.out.println("最终回调:" + type);
}
});
}
二 流的获取
public void testInput(){
// 直接定义流 1 2 3
Flux.just(1,2,3).subscribe(System.out::print);
// 按区间范围定义流 1 2 3 4
Flux.range(1,4).subscribe(System.out::print);
// 按时间间隔定义流,一秒发射一个 1 2 3 4 ...
Flux.interval(Duration.ofSeconds(1)).subscribe(System.out::print);
// 从数组生产流 1 2 3
Flux.fromArray(List.of(1,2,3).toArray());
// 从可迭代列表产生流
Flux.fromIterable(Arrays.asList(3,4,5,6)).subscribe(System.out::println);
// 直接定义流 1
Mono.just(1).doOnNext(System.out::println).subscribe();
// 直接定义可能为空的流
Mono.justOrEmpty(Optional.empty()).subscribe(System.out::println);
// 从有返回值回调函数产生流 传递数据、完成、和错误信号
Mono.fromCallable(() -> {
String a = "1,2,3,4";
// Integer b = 10/0;
return Arrays.stream(a.split(",")).count();
}).subscribe(
(item)-> System.out.println(item),
(err) -> System.out.println(err.getMessage()),
() -> System.out.println("程序执行完成")
);
// 执行无返回值回调函数 完成、和错误信号
Mono.fromRunnable(() -> {
String a = "1,2,3,4";
// Integer b = 10/0;
System.out.println(a.lenght()),
}).subscribe(
(item)-> System.out.println(item),
(err) -> System.out.println(err.getMessage()),
() -> System.out.println("程序执行完成")
);
// 延迟产生流,每次新的订阅者订阅都会执行回调函数产生对应流,属于懒汉型,而Flux.just() 属于恶汉型,后面有多少订阅都获取到同样的流
Flux<Date> defer = Flux.defer(() -> Mono.just(new Date()));
defer.subscribe(System.out::println); //11:46:17
Thread.currentThread().join(2000);
defer.subscribe(System.out::println); //11:46:19
}
注意:
1 empty()工厂方法,它们分别生成 Flux 或 Mono 的空实例。只传递成功和错误信号
2 never() 方法会创建一个永远不会发出完成、数据或错误等信号的流。
三 流的过滤
@Test void testFilter() throws InterruptedException {
// filter 过滤元素
Flux.range(1,10).filter(item -> item % 2 == 0).subscribe(System.out::println);
// ignoreElements 忽略所有元素,只返回完成和错误信号
Flux.range(1,10).map(item -> item /0).ignoreElements().subscribe(System.out::println,(err)-> System.out.println(err.getMessage()),()-> System.out.println("完成"));
// 获取前n个元素
Flux.range(1,10).take(3).subscribe(System.out::println);
// 获取后n个元素
Flux.range(1,10).takeLast(3).subscribe(System.out::println);
// 传递一个个元素直到满足某个条件。 以下打印 1 2 3 4 ,直到4满足条件时停止,包含4
Flux.range(1,10).takeUntil(item -> item == 4).subscribe(System.out::println);
// 用于获取序列的索引为 n 的元素。(0开始)
Flux.range(1,10).elementAt(2).subscribe(System.out::println);
// single 判断数据源发出是否单个数据项,单个数据项正常输出
Flux.range(1,2).single().subscribe(System.out::println); //多个元素的数据源发出IndexOutOfBoundsException 信号
Flux.range(1,1).single().subscribe(System.out::println);
Flux.range(1,0).single().subscribe(System.out::println); //空数据源发出 NoSuchElementException错误信号
Mono.just(1).single().subscribe(System.out::println);
Mono.empty().single().subscribe(System.out::println); // 空数据源发出 NoSuchElementException错误信号
// skipUntilOther 跳过当前流数据,直到另一个流发射出数据
Flux<String> source = Flux.interval(Duration.ofMillis(100))
.map(l -> "Source " + l)
.take(10);
Flux<String> other = Flux.interval(Duration.ofMillis(500))
.map(l -> "Other " + l)
.take(1);
source.skipUntilOther(other)
.subscribe(System.out::println);
// takeUntilOther 获取当前流数据,直到另一个流发射出数据
Flux<String> source1 = Flux.interval(Duration.ofMillis(100))
.map(l -> "Source " + l)
.take(10);
Flux<String> other2 = Flux.interval(Duration.ofMillis(500))
.map(l -> "Other " + l)
.take(1);
source.takeUntilOther(other)
.subscribe(System.out::println);
Thread.currentThread().join(10000);
}
四 流的收集
public void testCollect() throws InterruptedException {
// 将结果集合处理为流元素为List 的 Mono 流
Flux.just(1, 6, 2, 8, 3, 1, 5, 1).collectList().subscribe(System.out::println);
Flux.just(1, 6, 2, 8, 3, 1, 5, 1).collectSortedList(Comparator.reverseOrder()).subscribe(System.out::println);
// 将结果集合处理为流元素为Map 的 Mono 流
Flux.just(1, 2, 3, 4, 5).collectMap(item -> "key:num - " + item).subscribe(System.out::println);
Flux.just(1, 2, 3, 4, 5).collectMap(item -> "key:num - " + item,item -> "value:"+item).subscribe(System.out::println);
Flux.just(1, 2, 3, 4, 5).collectMap(
integer -> "key:" + integer,
integer -> "value:" + integer,
() -> {
Map<String, String> map = new HashMap<>();
for (int i = 0; i < 5; i++) {
map.put(i + "key", i + "value");
}
return map;
}).subscribe(System.out::println);
Flux.just(1, 2, 3, 4, 5)
.collectMultimap(
item -> "key:" + item,
item1 -> {
List<String> values = new ArrayList<>();
for (int i = 0; i < item1; i++) {
values.add("value" + i);
}
return values;
})
.subscribe(System.out::println);
Flux.just(1, 2, 3, 4, 5)
.collectMultimap(
item -> "key:" + item,
item1 -> {
List<String> values = new ArrayList<>();
for (int i = 0; i < item1; i++) {
values.add("value" + i);
}
return values;
},
() -> {
Map map = new HashMap<String, List<String>>();
List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.clear();
for (int j = 0; j < i; j++) {
list.add("ele:" + j);
}
map.put(i + ":key", list);
}
return map;
}
).subscribe(System.out::println);
Thread.currentThread().join(10000);
}
五 流的转换
public void testOther() throws InterruptedException {
// 空流时处理
Flux.empty().defaultIfEmpty("hello").subscribe(System.out::println);
Mono
// .just(null) //流里面有一个null值元素
.empty() //流里面没有元素,只有完成信号/结束信号
.switchIfEmpty(Mono.just(2)) // 如果流为empty,指定一个默认值代替,如果不是,用发布者的值
.doOnEach(objectSignal -> {
System.out.println(objectSignal);
})
.subscribe(item -> System.out.println(item));
//流去重
// repeat 流重复 实际上是重复了四次,1次原始的,3次重复的。
Flux.just(1, 2, 3).repeat(3).subscribe(System.out::println);
// repeat 全局去重
Flux.just(1, 2, 3).repeat(3).distinct().subscribe(System.out::println);
// distinctUntilChanged 顺序去重,只去除相邻重复项
Flux.just(1, 1, 2, 2, 3, 3, 1, 1) //1 2 3 1
.distinctUntilChanged()
.subscribe(System.out::println);
// count 获取流元素数量
Flux.just(1, 1, 2, 2, 3, 3, 1, 1).count().subscribe(System.out::println);
//流判断
// 判断流元素是否都是所需元素
Flux.just(1, 1, 2).all(item -> item <= 2).subscribe(System.out::println);
// 判断流元素是否包含至少一个所需元素
Flux.just(1, 1, 2).any(item -> item % 2 == 0).subscribe(System.out::println);
// 判断流中是否有对应元素
Flux.just(1, 1, 4).hasElement(2).subscribe(System.out::println);
// 判断流中是否包含多个元素(1-n)
Flux.just(1,2).hasElements().subscribe(System.out::println);
// 流排序
Flux.just(1,2,4,3).sort().subscribe(System.out::println);
Flux.just(1,2,4,3).sort((a,b) -> a < b ? 1:-1).subscribe(System.out::println);
// 流聚合
// 流聚合操作 只生成一个具有最终结果的元素 打印15
Flux.range(1, 5).reduce(0, (item1, item2) -> item1 + item2).subscribe(System.out::println);
// 流聚合操作 向下游发送中间结果 打印 0 1 3 6 10 15
Flux.range(1, 5).scan(0, (num1, num2) -> num1 + num2).subscribe(System.out::println);
Thread.currentThread().join(10000);
六 流的组合
6.1 流合并
1 concat 操作符通过向下游转发接收的元素来连接所有数据源。当操作符连接两个流时,它首 先消费并重新发送第一个流的所有元素,然后对第二个流执行相同的操作。
2 merge 操作符将来自上游序列的数据合并到一个下游序列中。与 concat 操作符不同,上游数 据源是立即(同时)被订阅的。
public void testMerge() throws InterruptedException {
//concat 合并流 将后面的流追加到前面的流的基础上 1,2,3,4,5
Flux.concat(Flux.just(1,2,3).delayElements(Duration.ofSeconds(2)),Flux.just(4,5).delayElements(Duration.ofSeconds(1))).subscribe(System.out::println);
Flux.just(1,2,3).concatWith(Flux.just(4,5)).subscribe(System.out::println);
//1-> 1 2 3 2-> 1 2 3 ...
Flux.just(1,2,3).concatMap(item -> Flux.just(item+"->",1,2)).subscribe(System.out::println);
//merge 按照时间序列进行合并 1 4 2 3 5 6
Flux.merge(Flux.just(1,2,3).delayElements(Duration.ofSeconds(1)),Flux.just(4,5,6).delayElements(Duration.ofSeconds(2)))
.subscribe(System.out::println);
Flux.just(1,2,3).delayElements(Duration.ofSeconds(1)).mergeWith(Flux.just(4,5,6).delayElements(Duration.ofSeconds(2)))
.subscribe(System.out::println);
//mergeSequential 按照哪个流先发元素排队 1 2 3 4 5 6
Flux.mergeSequential(Flux.just(1,2,3).delayElements(Duration.ofSeconds(2)),Flux.just(4,5,6).delayElements(Duration.ofSeconds(1)))
.subscribe(System.out::println);
Thread.currentThread().join(10000);
}
6.2 流压缩
1 zip 操作符订阅所有上游,等待所有数据源发出一个元素,然后将接收到的元素组合到一个 输出元素中。
2 combineLatest 操作符与 zip 操作符的工作方式类似。但是,只要至少一个上游数据源发 出一个值,它就会生成一个新值。
//最多支持8流压缩;压缩成n元组
public void testZip(){
// 按照定义的发布者个数将其个数封装成n元组,当其中一个发布者元素消费完既取消其他发布者数据发送
//1---3----31 2---4----41
Flux.zip(Flux.just(1,2,6),Flux.just(3,4,5),Flux.just(31,41))
.log()
.map(tuple -> {
return tuple.getT1() + "---" + tuple.getT2() + "----" + tuple.getT3();
})
.subscribe(System.out::println);
// 与上同理,但是每次只能压缩两个流
Flux.just(1,2,3)
.zipWith(Flux.just(3,4,5)) //[1,3] [2,4] [3,5]
.zipWith(Flux.just(3,4,5)) //[[1,3],3] [[2,4],4] [[3,5],5]
.subscribe(System.out::println);
// 压缩流时候可以自定义合并规则
Flux.just(1,2,3)
.zipWith(Flux.just(3,4,5),(a,b)->{
return a + "---" + b;
})
.subscribe(System.out::println);
Flux.combineLatest(
Flux.range(1, 100).delayElements(Duration.ofMillis(100)),
Flux.range(1000, 100).delayElements(Duration.ofMillis(250)),
(integer, integer2) -> integer + "---" + integer2
)
.subscribe(System.out::println);
}
七 转换新流
7.1 handle和transform方式
// handle方式
public void testHandle(){
Flux.range(1,10)
.handle((value,sink) -> {
System.out.println("当前数据:" + value);
sink.next("新数据:" + value);
})
.log()
.subscribe();
}
// transform方式
public void testTransform(){
Flux<String> flux = Flux
.just("a", "b", "c")
// 将老流转换成新流
.transform(items -> { // items 直接获取流所有元素Flux
return items.map(String::toUpperCase);
});
flux.subscribe(item -> System.out.println("订阅者1-》" +item));
flux.subscribe(item -> System.out.println("订阅者2-》" +item));
}
7.2 thenxxx方式
Mono 和 Flux 流都有 then、thenMany 和 thenEmpty 操作符,待上游流完成处理后,触发新流,订阅是对于新流的。
public void testThen() throws InterruptedException {
// then 待上游完成生成一个新Mono流
Flux.just(1, 2, 3)
.doOnNext(item -> System.out.println("副作用:" + item))
.then(Mono.just(4))
.subscribe(System.out::println);
// thenMany 待上游完成生成一个新Flux流
Flux.just(1, 2, 3)
.doOnNext(item -> System.out.println("副作用:" + item))
.thenMany(Flux.just(4, 5, 6))
.subscribe(System.out::println);
// thenEmpty 待上游完成生成一个新空流
Flux.just(1, 2, 3)
.doOnNext(item -> System.out.println("副作用:" + item))
.thenEmpty(Mono.empty())
.subscribe(System.out::println);
Thread.currentThread().join(10000);
}
八 流扁平化
public void testFlatMap() {
Flux.just("zhang san", "li si")
.flatMap(v -> {
String[] s = v.split(" ");
return Flux.fromArray(s); //把数据包装成多元素流
})
.log()
.subscribe();//两个人的名字,按照空格拆分,打印出所有的姓与名
}
public void testMap() throws InterruptedException {
// flatMap 允许元素交错
Random random = new Random();
Flux.just(Arrays.asList(1, 2, 3), Arrays.asList("a", "b", "c", "d"),
Arrays.asList(7, 8, 9))
.doOnNext(System.out::println)
.flatMap(item -> Flux.fromIterable(item)
.delayElements(Duration.ofMillis(random.nextInt(100) + 100))
.doOnSubscribe(subscription -> {
System.out.println("已订阅");
})
)
.subscribe(System.out::println);
Flux.just(Arrays.asList(1, 2, 3), Arrays.asList("a", "b", "c", "d"),
Arrays.asList(7, 8, 9))
.doOnNext(System.out::println)
.concatMap(item -> Flux.fromIterable(item)
.delayElements(Duration.ofMillis(random.nextInt(100) + 100))
.doOnSubscribe(subscription -> {
System.out.println("已订阅");
}))
.subscribe(System.out::println);
Flux.just(Arrays.asList(1, 2, 3), Arrays.asList("a", "b", "c", "d"),
Arrays.asList(7, 8, 9))
.doOnNext(System.out::println)
.flatMapSequential(item -> Flux.fromIterable(item)
.delayElements(Duration.ofMillis(random.nextInt(100) + 100))
.doOnSubscribe(subscription -> {
System.out.println("已订阅");
}))
.subscribe(System.out::println);
Thread.sleep(18000);
}
注意:
1. 操作符是否立即订阅其内部流; flatMap 操作符和 flatMapSequential 操作符会立即订阅,而 concatMap 操作符则会在生成 下一个子流并订阅它之前等待每个内部完成。
2. 操作符是否保留生成元素的顺序; concatMap 天生保留与源元素相同的顺序,flatMapSequential 操作符通过对所接收的元素进 行排序来保留顺序,而 flatMap 操作符不一定保留原始排序。
3. 操作符是否允许对来自不同子流的元素进行交错; flatMap 操作符允许交错,而 concatMap和 flatMapSequential 不允许交错。
4 有时也作为新流产生方法
九 流批量处理
public void testBatch() throws InterruptedException {
// buffer 符将许多事件收集到一个事件集合中 结果流的类型为 Flux<List<T>>
Flux.range(1, 100)
.buffer(5)
.subscribe(System.out::println);
Flux.range(1,20)
.delayElements(Duration.ofSeconds(1))
.buffer(5) //收集满5个传到下一个级
.subscribe(System.out::println);
//window 开窗拆分,根据元素个数或者时间进行开窗 结果流的类型为 Flux<Flux<T>>
Flux.just(1,2,3,4,5).window(3).toIterable().forEach(item -> {
item.doOnNext(System.out::println).subscribe();
System.out.println("------");
});
Flux.just(1,2,3,4,5,6).delayElements(Duration.ofSeconds(1)).window(Duration.ofSeconds(3)).subscribe(item -> {
item.doOnNext(System.out::println).subscribe();
System.out.println("------");
});
// windowUntil 如果需要根据序列中的元素是否满足某一条件进行开窗拆分 第二个参数主要确定完成信号在切割窗口之前(true),还是切割窗口之后(false)
Flux.just(1,2,3,4,5,6).windowUntil(item -> item % 5 == 0,true).subscribe(item -> {
item.doOnNext(System.out::println).doOnComplete(() -> System.out.println("流完成")).subscribe(); // 打印 1 2 3 4 流完成 5 | 6 流完成
System.out.println("------");
});
// group 通过某些条件对响应式流中的元素进行分组,通过对每个元素打一个标签(key),按照标签将元素进行分组。
Flux.range(1, 7)
.groupBy(item -> item % 2 == 0 ? "偶数" : "奇数")
.subscribe(groupFlux -> groupFlux.scan(
new ArrayList<Integer>(),
(list, element) -> {
list.add(element);
return list;
}
).filter(list -> !list.isEmpty())
.subscribe(item -> System.out.println(groupFlux.key() + " <---> " + item))
);
Thread.sleep(800);
}
注意:window操作符和buffer操作符类似,后者仅在缓冲区关闭时才会发出集合,而 window 操作符会在事件到达时立即对其进行传播,以更快地做出响应并实现更复杂的工作流程。
十 响应式流转化为阻塞结构
1. toIterable 方法将响应式 Flux 转换为阻塞 Iterable。
2. toStream 方法将响应式 Flux 转换为阻塞 Stream API。从 Reactor 3.2 开始,在底层使用 toIterable 方法。
3. blockFirst 方法阻塞了当前线程,直到上游发出第一个值或完成流为止。
4. blockLast 方法阻塞当前线程,直到上游发出最后一个值或完成流为止。在 onError的情况 下,它会在被阻塞的线程中抛出异常。
public void testBlock() throws InterruptedException {
Flux<Integer> just = Flux.just(1, 2, 3, 4);
List<Integer> list = just.collectList().block();
System.out.println(list);
final Iterable<Integer> integers = Flux.just(1, 2, 3)
.delayElements(Duration.ofSeconds(1))
.toIterable();
System.out.println("==================");
for (Integer integer : integers) {
System.out.println(integer);
}
System.out.println("==================");
Stream<Integer> integerStream = Flux.just(1, 2, 3)
.delayElements(Duration.ofSeconds(1))
.toStream();
System.out.println("==================");
integerStream.forEach(System.out::println);
System.out.println("==================");
Integer integer = Flux.just(1, 2, 3)
.delayElements(Duration.ofSeconds(1))
.doOnNext(System.out::println)
.blockFirst();
System.out.println("==============");
System.out.println(integer);
System.out.println("==============");
Thread.sleep(5000);
// 该方法不会阻塞主线程
Flux.just(1, 2, 3)
.delayElements(Duration.ofSeconds(1))
.doOnEach(System.out::println)
.subscribe();
// 该方法阻塞,直到流处理到最后一个元素
Integer integer1 = Flux.just(1, 2, 3)
.delayElements(Duration.ofSeconds(1))
.doOnEach(System.out::println)
.blockLast();
System.out.println("================");
System.out.println(integer1);
System.out.println("================");
Flux<Integer> integerFlux = Flux.just(1, 2,
3).delayElements(Duration.ofSeconds(1));
integerFlux.subscribe(item -> System.out.println("第一个订阅:" + item));
integerFlux.subscribe(item -> System.out.println("第二个订阅:" + item));
integerFlux.subscribe(item -> System.out.println("第三个订阅:" + item));
final Integer integer2 = integerFlux.blockLast();
System.out.println("阻塞等待最后一个元素" + integer2);
System.out.println("-=-=-=-=-=-=-=-=-");
Thread.sleep(5000);
Thread.sleep(18000);
}
十一 物化和非物化信号
将流中的元素封装为Signal对象进行处理。
1 materialize 物化信号
2 dematerialize 非物化信号
public void testMaterialize() throws InterruptedException {
Flux.just(1, 2, 3).delayElements(Duration.ofMillis(1000))
.publishOn(Schedulers.parallel())
.concatWith(Flux.error(new Exception("手动异常")))
.materialize()
// .dematerialize()
.doOnEach(item -> System.out.println(item.isOnComplete()))
.subscribe(System.out::println);
Thread.sleep(18000);
}
打印结果
十二 背压处理
12.1 通过不同策略
public void testBackpressure() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
Flux.range(1, 100)
.delayElements(Duration.ofMillis(10))
.onBackpressureBuffer(50) // 设置大小为50缓冲区
// .onBackpressureDrop()
// .onBackpressureLatest()
// .onBackpressureError()
.delayElements(Duration.ofMillis(100))
.subscribe(
System.out::println,
ex -> {
System.out.println(ex);
latch.countDown();
},
() -> {
System.out.println("处理完成");
latch.countDown();
}
);
latch.await();
System.out.println("main结束");
}
12.2 通过limitRate限流
public void TestLimit(){
Flux.range(1,1000)
.log()
//限制发布者向下游发布的数据数量 onNext(1)
.limitRate(100) //第一次预取request(100)个 后续预取request(75)个
.limitRate(100,20) //第一次预取request(100)个 后续预取request(20)个
.subscribe();
}
十三 冷数据流多订阅优化处理
冷发布者行为方式:无论订阅者何时出现,都为该订阅者生成所有序列数据,没有订阅者就不会生 成数据。
热发布者中的数据生成不依赖于订阅者而存在。因此,热发布者可能在第一个订阅者出现之前开始 生成元素。
13.1 无处理流元素
每次订阅者的订阅都会重新生成数据流
public void testCoolDate() throws InterruptedException {
Flux<Integer> source = Flux.range(0, 5)
.doOnSubscribe(
s -> System.out.println("冷发布者订阅数据")
);
source.subscribe(item -> System.out.println("s1: " + item));
source.subscribe(item -> System.out.println("s2: " + item));
Thread.sleep(18000);
}
13.2 多播流元素
通过响应式转换将冷发布者转变为热发布者。
public void testCoolDate() throws InterruptedException {
Flux<Integer> source = Flux.range(0, 5)
.doOnSubscribe(
s -> System.out.println("冷发布者订阅数据")
);
final ConnectableFlux<Integer> sourcePublish = source.publish();
sourcePublish.subscribe(item -> System.out.println("s1: " + item));
sourcePublish.subscribe(item -> System.out.println("s2: " + item));
System.out.println("所有订阅者都准备好建立连接了");
sourcePublish.connect();
Thread.sleep(18000);
}
13.2 缓存流元素
可以调整缓存所能容纳的数据量以及每个缓存项的到期时间。
public void testCoolDate() throws InterruptedException {
Flux<Integer> source = Flux.range(0, 5)
.doOnSubscribe(
s -> System.out.println("冷发布者订阅数据")
);
Flux<Integer> cache = source.cache(Duration.ofMillis(1000));
cache.subscribe(item -> System.out.println("s1: " + item));
cache.subscribe(item -> System.out.println("s2: " + item));
Thread.sleep(1200);
cache.subscribe(item -> System.out.println("s3: " + item));
Thread.sleep(18000);
}
前两个订阅者共享第一个订阅的同一份缓存数据。然后,在一定延迟之后,由于第三个订阅者无法 获取缓存数据,因此一个针对冷发布者的新订阅被触发了。最后,即使该数据不来自缓存,第三个订阅 者也接收到了所需的数据。
13.3 共享流元素
share 操作符可以将冷发布者转变为热发布者。该操作符会为每个新订阅者传播订阅者尚未错过的 事件。
public void testCoolDate() throws InterruptedException {
Flux<Integer> source = Flux.range(0, 10)
.delayElements(Duration.ofMillis(100))
.doOnSubscribe(s -> System.out.println("冷发布者新的订阅票据"));
Flux<Integer> share = source.share();
share.subscribe(item -> System.out.println("s1: " + item));
Thread.sleep(500);
share.subscribe(item -> System.out.println("s2: " + item));
Thread.sleep(18000);
}
十四 时间处理
public void testTime() throws InterruptedException {
// interval 基于一定时间间隔持续生成事件
Flux.interval(Duration.ofSeconds(1)).subscribe((item) -> System.out.println(Thread.currentThread().getName()+":"+item));
Flux.interval(Duration.ofSeconds(3), Duration.ofMillis(100)).subscribe(System.out::println);
Flux.interval(Duration.ofMillis(100), Schedulers.newSingle("count"))
.subscribe(
item -> {
System.out.println(Thread.currentThread().getName() + " -- " + item);
}
);
// delayElements 生成延迟元素
Flux.range(1, 10)
.delayElements(Duration.ofSeconds(1))
.subscribe(item -> {
System.out.println(Thread.currentThread().getName() + " -- " + item);
});
// delaySequence 延迟所有信号
Flux.range(1, 10)
.delaySequence(Duration.ofSeconds(10))
.log()
.subscribe(item -> {
System.out.println(Thread.currentThread().getName() + " -- " + item);
}
);
Thread.sleep(18000);
}
十五 每次消费流元素缓冲区 buffer
public void TestBuffer(){
Flux.range(1,10)
//默认一个一个处理数据 onNext(1)
//request(unbounded)一次性获取发布者数据 onNext([1, 2, 3]) 按照缓冲区个数处理数据,消费一次最多可以拿到三个元素
.buffer(3)
.log()
.subscribe(item -> System.out.println(item));
}
十六 流的调度 publishOn
public void testPublishOn(){
Scheduler s = Schedulers.newParallel("parallel-scheduler", 4);
final Flux<String> flux = Flux
.range(1, 2)
.map(i -> 10 + i)
.log()
.publishOn(s)
.map(i -> "value " + i);
//只要不指定线程池,默认发布者用的线程就是订阅者的线程;
new Thread(() -> flux.subscribe(System.out::println)).start();
try {
Thread.currentThread().join(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
十七 异常处理
17.1 onErrorReturn
吃掉异常 返回一个代替默认值,流中断
public void testException(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
//吃掉异常 返回一个代替默认值
.onErrorReturn(ArithmeticException.class,10)
.subscribe(
System.out::println,
err -> {
System.out.println(err);
}
);
}
17.2 onErrorResume
吃掉异常,调度回调函数获取其返回值作为默认值,流中断
public void testException1(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
.onErrorResume(err -> {
System.out.println(err);
return Flux.just(15,19);
})
.log()
.subscribe(
System.out::println,
err -> {
System.out.println(err);
},
() -> {
System.out.println("流正常结束");
}
);
}
public void testException2(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
.onErrorResume(err -> {
System.out.println(err);
return Flux.error(new RuntimeException("数字计算错误!"));
})
.log()
.subscribe(
System.out::println,
err -> {
System.out.println(err);
},
() -> {
System.out.println("流正常结束");
}
);
}
17.3 onErrorMap
吃掉异常,抱着一个新的异常返回,流中断
public void testException3(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
.onErrorMap(err -> new RuntimeException(err.getMessage()+"包装异常!"))
.subscribe(
System.out::println,
err -> {
System.out.println(err);
},
() -> {
System.out.println("流正常结束");
}
);
}
17.4 onErrorContinue
不吃掉异常,处理异常数据,流不中断
public void testException5(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
.onErrorContinue((err,value)->{
System.out.println(err);
System.out.println(value+"出问题了,我会记录");
})
.subscribe(
System.out::println,
err -> {
System.out.println("流异常结束");
System.out.println(err);
},
() -> {
System.out.println("流正常结束");
}
);
}
17.5 onErrorComplete
把错误结束信号替换为正常结束信号; 正常结束
public void testException6(){
Flux.just(1,2,0,4)
.map(item -> 10/item)
.onErrorComplete() //把错误结束信号替换为正常结束信号; 正常结束
//.onErrorStop: 错误后停止流. 源头中断,所有监听者全部结束; 错误结束
.subscribe(
System.out::println,
err -> {
System.out.println("流异常结束");
System.out.println(err);
},
() -> {
System.out.println("流正常结束");
}
);
}
十八 上下文 Context (threadLocal)
public void testContext(){
Flux.just(1,2,0,4)
.map(item -> item*10)
.transformDeferredContextual((flux,context) -> {
return flux.map(item -> item + "" +context.get("prefix"));
})
.contextWrite(Context.of("prefix","哈哈"))
.subscribe(System.out::println);
}
十九 信号处理 doOnxxx
如果信号处理和自定义订阅者对应信号相同时,先处理doOnxx信号处理,再处理订阅者的。
/**
* 信号: 正常/异常(取消)
* SignalType:
* SUBSCRIBE: 被订阅
* REQUEST: 请求了N个元素
* CANCEL: 流被取消
* ON_SUBSCRIBE:在订阅时候
* ON_NEXT: 在元素到达
* ON_ERROR: 在流错误
* ON_COMPLETE:在流正常完成时
* AFTER_TERMINATE:中断以后
* CURRENT_CONTEXT:当前上下文
* ON_CONTEXT:感知上下文
*
* doOnXxx API触发时机
* 1、doOnNext:每个数据(流的数据)到达的时候触发
* 2、doOnEach:每个元素(流的数据和信号)到达的时候触发 该方法处理表示响应式流领域的所有信号,包括 onError、onSubscribe、onNext、onError 和 onComplete。
* 3、doOnRequest: 消费者请求流元素的时候
* 4、doOnError:流发生错误
* 5、doOnSubscribe: 流被订阅的时候
* 6、doOnTerminate: 发送取消/异常信号中断了流
* 7、doOnCancle: 流被取消
* 8、doOnDiscard:流中元素被忽略的时候
*
*/
public void testDoOnxxx() throws InterruptedException {
Flux.just(1,2,0,4,5)
.delayElements(Duration.ofSeconds(1))
.map(item -> 10/item)
.map(item -> item + "qqqqqqqq")
.doOnError(err -> { // 先执行,再执行subscript的
System.out.println("出现异常---记录异常");
}).doOnCancel(() -> { //
System.out.println("流取消!");
}).doOnComplete(()->{
System.out.println("流正常结束");
}).doOnNext(value -> {
System.out.println("next-"+value);
}).doOnEach(value -> {
System.out.println("each-"+value);
})
.doFinally(signalType -> { // subscript执行后再执行
System.out.println("流结束信号:"+signalType);
})
.subscribe(
System.out::println,
err -> {
System.out.println("消费者流错误"+err);
},
() -> {
System.out.println("消费者流正常结束");
}
);
Thread.currentThread().join(10000);
}
二十 重试和超时
public void testRetryAndTimeout() throws InterruptedException, IOException {
Flux.interval(Duration.ofSeconds(2))
.log()
.map(item -> item + "111111")
.timeout(Duration.ofSeconds(2)) //
.retry(2) //每次重试从头开始
.onErrorReturn("10000")
.subscribe(System.out::println);
Thread.currentThread().join(10000);
// System.in.read();
}