Reactor 3 学习笔记(2)
接上篇继续学习各种方法:
4.9、reduce/reduceWith
@Test
public void reduceTest() {
Flux.range(1, 10).reduce((x, y) -> x + y).subscribe(System.out::println);
Flux.range(1, 10).reduceWith(() -> 10, (x, y) -> x + y).subscribe(System.out::println);
}
输出:
55
65
上面的代码,reduce相当于把1到10累加求和,reduceWith则是先指定一个起始值,然后在这个起始值基础上再累加。(tips: 除了累加,还可以做阶乘)
reduce示意图:

reduceWith示意图:

4.10、merge/mergeSequential/contact
@Test
public void mergeTest() {
Flux.merge(Flux.interval(Duration.of(500, ChronoUnit.MILLIS)).take(5),
Flux.interval(Duration.of(600, ChronoUnit.MILLIS), Duration.of(500, ChronoUnit.MILLIS)).take(5))
.toStream().forEach(System.out::println); System.out.println("-----------------------------"); Flux.mergeSequential(Flux.interval(Duration.of(500, ChronoUnit.MILLIS)).take(5),
Flux.interval(Duration.of(600, ChronoUnit.MILLIS), Duration.of(500, ChronoUnit.MILLIS)).take(5))
.toStream().forEach(System.out::println);
}
merge就是将把多个Flux"按元素实际产生的顺序"合并,而mergeSequential则是按多个Flux"被订阅的顺序"来合并,以上面的代码来说,二个Flux,从时间上看,元素是交替产生的,所以merge的输出结果,是混在一起的,而mergeSequential则是能分出Flux整体的先后顺序。
0
0
1
1
2
2
3
3
4
4
-----------------------------
0
1
2
3
4
0
1
2
3
4
merge示意图:

mergeSequential示意图:

与mergeSequential类似的,还有一个contact方法,示意图如下:

4.11、combineLatest
@Test
public void combineLatestTest() {
Flux.combineLatest(
Arrays::toString,
Flux.interval(Duration.of(10000, ChronoUnit.MILLIS)).take(3),
Flux.just("A", "B"))
.toStream().forEach(System.out::println); System.out.println("------------------"); Flux.combineLatest(
Arrays::toString,
Flux.just(0, 1),
Flux.just("A", "B"))
.toStream().forEach(System.out::println); System.out.println("------------------"); Flux.combineLatest(
Arrays::toString,
Flux.interval(Duration.of(1000, ChronoUnit.MILLIS)).take(2),
Flux.interval(Duration.of(10000, ChronoUnit.MILLIS)).take(2))
.toStream().forEach(System.out::println);
}
该操作会将所有流中的最新产生的元素合并成一个新的元素,作为返回结果流中的元素。只要其中任何一个流中产生了新的元素,合并操作就会被执行一次。
分析一下第一段输出:
第1个Flux用了延时生成,第1个数字0,10秒后才产生,这时第2个Flux中的A,B早就生成完毕,所以此时二个Flux中最新生在的元素,就是[0,B],类似的,10秒后,第2个数字1依次产生,再执行1次合并,生成[1,B]...
输出:
[0, B]
[1, B]
[2, B]
------------------
[1, A]
[1, B]
------------------
[1, 0]
[1, 1]
示意图如下:

4.12、first
@Test
public void firstTest() {
Flux.first(Flux.fromArray(new String[]{"A", "B"}),
Flux.just(1, 2, 3))
.subscribe(System.out::println);
}
这个很简单理解,多个Flux,只取第1个Flux的元素。输出如下:
A
B
示意图:
4.13、 map
@Test
public void mapTest() {
Flux.just('A', 'B', 'C').map(a -> (int) (a)).subscribe(System.out::println);
}
map相当于把一种类型的元素序列,转换成另一种类型,输出如下:
65
66
67
示意图:
五、消息处理
写代码时,难免会遇到各种异常或错误,所谓消息处理,就是指如何处理这些异常。
5.1 订阅错误消息
@Test
public void subscribeTest1() {
Flux.just("A", "B", "C")
.concatWith(Flux.error(new IndexOutOfBoundsException("下标越界啦!")))
.subscribe(System.out::println, System.err::println);
}
注意:这里subscribe第2个参数,指定了System.err::println ,即错误消息,输出到异常控制台上。
输出效果:

示意图:

5.2 onErrorReturn
即:遇到错误时,用其它指定值返回
@Test
public void subscribeTest2() {
Flux.just("A", "B", "C")
.concatWith(Flux.error(new IndexOutOfBoundsException("下标越界啦!")))
.onErrorReturn("X")
.subscribe(System.out::println, System.err::println);
}
输出:
A
B
C
X
示意图:

5.3 onErrorResume
跟onErrorReturn有点接近,但更灵活,可以根据异常的类型,有选择性的处理返回值。
@Test
public void subscribeTest3() {
Flux.just("A", "B", "C")
.concatWith(Flux.error(new IndexOutOfBoundsException("下标越界啦!")))
.onErrorResume(e -> {
if (e instanceof IndexOutOfBoundsException) {
return Flux.just("X", "Y", "Z");
} else {
return Mono.empty();
}
})
.subscribe(System.out::println, System.err::println);
}
输出:
A
B
C
X
Y
Z
示意图:
5.4 retry
即:遇到异常,就重试。
@Test
public void subscribeTest4() {
Flux.just("A", "B", "C")
.concatWith(Flux.error(new IndexOutOfBoundsException("下标越界啦!")))
.retry(1)
.subscribe(System.out::println, System.err::println);
}
输出:

示意图:

六、(线程)调度器
reactor中到处充满了异步调用,内部必然有一堆线程调度,Schedulers提供了如下几种调用策略:
6.1 Schedulers.immediate() - 使用当前线程
6.2 Schedulers.elastic() - 使用线程池
6.3 Schedulers.newElastic("test1") - 使用(新)线程池(可以指定名称,更方便调试)
6.4 Schedulers.single() - 单个线程
6.5 Schedulers.newSingle("test2") - (新)单个线程(可以指定名称,更方便调试)
6.6 Schedulers.parallel() - 使用并行处理的线程池(取决于CPU核数)
6.7 Schedulers.newParallel("test3") - 使用并行处理的线程池(取决于CPU核数,可以指定名称,方便调试)
6.8 Schedulers.fromExecutorService(Executors.newScheduledThreadPool(5)) - 使用Executor(这个最灵活)
示例代码:
@Test
public void schedulesTest() {
Flux.fromArray(new String[]{"A", "B", "C", "D"})
.publishOn(Schedulers.newSingle("TEST-SINGLE", true))
.map(x -> String.format("[%s]: %s", Thread.currentThread().getName(), x))
.toStream()
.forEach(System.out::println);
}
输出:
[TEST-SINGLE-1]: A
[TEST-SINGLE-1]: B
[TEST-SINGLE-1]: C
[TEST-SINGLE-1]: D
七、测试&调试
异步处理,通常是比较难测试的,reactor提供了StepVerifier工具来进行测试。
7.1 常规单元测试
@Test
public void stepTest() {
StepVerifier.create(Flux.just(1, 2)
.concatWith(Mono.error(new IndexOutOfBoundsException("test")))
.onErrorReturn(3))
.expectNext(1)
.expectNext(2)
.expectNext(3)
.verifyComplete();
}
上面的示例,Flux先生成1,2这两个元素,然后抛了个错误,但马上用onErrorReturn处理了异常,所以最终应该是期待1,2,3,complete这样的序列。
7.2 模拟时间流逝
Flux.interval这类延时操作,如果延时较大,比如几个小时之类的,要真实模拟的话,效率很低,StepVerifier提供了withVirtualTime方法,来模拟加快时间的流逝(是不是很体贴^_^)
@Test
public void stepTest2() {
StepVerifier.withVirtualTime(() -> Flux.interval(Duration.of(10, ChronoUnit.MINUTES),
Duration.of(5, ChronoUnit.SECONDS))
.take(2))
.expectSubscription()
.expectNoEvent(Duration.of(10, ChronoUnit.MINUTES))
.thenAwait(Duration.of(5, ChronoUnit.SECONDS))
.expectNext(0L)
.thenAwait(Duration.of(5, ChronoUnit.SECONDS))
.expectNext(1L)
.verifyComplete();
}
上面这个Flux先停10分钟,然后每隔5秒生成一个数字,然后取前2个数字。代码先调用
expectSubscription 期待流被订阅,然后
expectNoEvent(Duration.of(10, ChronoUnit.MINUTES)) 期望10分钟内,无任何事件(即:验证Flux先暂停10分钟),然后
thenAwait(Duration.of(5, ChronoUnit.SECONDS)) 等5秒钟,这时已经生成了数字0
expectNext(0L) 期待0L
... 后面的大家自行理解吧。
7.3 记录日志
@Test
public void publisherTest() {
Flux.just(1, 0)
.map(c -> 1 / c)
.log("MY-TEST")
.subscribe(System.out::println);
}
输出:

示意图:

7.4 checkpoint检查点
可以在一些怀疑的地方,加上checkpoint检查,参考下面的代码:
@Test
public void publisherTest() {
Flux.just(1, 0)
.map(c -> 1 / c)
.checkpoint("AAA")
.subscribe(System.out::println);
}
输出:

Reactor 3 学习笔记(2)的更多相关文章
- Reactor 3 学习笔记(1)
Reactor 3 与之前学习的RxJava是同一类(反应式编程)框架,基本概念大致差不多,简单记录一下: Reactor 3 利用了java 8中的CompletableFuture.Stream. ...
- muduo学习笔记(二)Reactor关键结构
目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...
- Mudo C++网络库第六章学习笔记
muduo网络库简介 高级语言(Java, Python等)的Sockects库并没有对Sockects API提供更高层的封装, 直接用它编写程序很容易掉到陷阱中: 网络库的价值还在于能方便地处理并 ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- muduo网络库学习笔记(三)TimerQueue定时器队列
目录 muduo网络库学习笔记(三)TimerQueue定时器队列 Linux中的时间函数 timerfd简单使用介绍 timerfd示例 muduo中对timerfd的封装 TimerQueue的结 ...
- Netty学习笔记(二)——netty组件及其用法
1.Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 原生NIO存在的问题 1) NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.Se ...
- 《精通并发与Netty》学习笔记(01 - netty介绍及环境搭建)
一.Netty介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. ...
- Netty学习笔记-入门版
目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
随机推荐
- android margin--负的margin的使用
通常情况下,如果我们想要两个控件实现重叠的效果,一般都是使用FrameLayout 或者RelativeLayout布局.其实,如果设置两个控件的margin值为负数,也能实显控件重叠的效果. 先展示 ...
- Ex 6_12 凸多边形的最优三角剖分..._第六次作业
假设顶点的总数为n,从0到n-1. 从序号为0的顶点开始以逆时针方向排序,对于 令子问题A[i,j]为包含顶点i,i+1, . . . j的凸多边形的最小三角剖分代价,dist(i,j)为顶点i到顶点 ...
- LeetCode(5):最长回文子串
Medium! 题目描述: 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 长度最长为1000. 示例: 输入: "babad" 输出: "bab&quo ...
- 关于Newtonsoft.Json,反序列化jason,内容有key的转换
Newtonsoft.Json,反序列化,对于result里面的结果,可以使用Dictionary<string, List<类名>>,string是key值,value又是一 ...
- python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))
昨日内容回顾 1. 三个类 ChangeList,封装列表页面需要的所有数据. StarkConfig,生成URL和视图对应关系 + 默认配置 AdminSite,用于保存 数据库类 和 处理该类的对 ...
- Windows系统下oracle数据库每天定时备份
第一步:建立备份脚本oraclebackup.bat 首先建立一个备份bat文件,在D盘下新建备份目录oraclebackup,将oracle安装目录下的EXP.EXE复制到此目录下,再新建一个文本文 ...
- [NOI2012]随机数生成器
题解: 很显然是一道矩阵优化dp 然而表示我很智障地把式子一个个带入 然后就发现了为什么会有那些部分分(大概用扩欧是70吧) 注意用矩阵计算的时候要用快速乘(当然想写高精那也随便,时间无限宽裕) 代码 ...
- 利用dwebsocket在Django中使用Websocket
一.Websockets介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...
- POJ-1456 Supermarket 销售商品【贪心】+【并查集】
题目链接:http://poj.org/problem?id=1456 题目大意: 有N件商品,分别给出商品的价值和销售的最后期限,只要在最后日期之前销售处,就能得到相应的利润,并且销售该商品需要1天 ...
- ES6 中的 Map和Set
集合的概念以及和数组的区别 其实数组也是集合, 只不过数组的索引是数值类型.当想用非数值类型作为索引时, 数组就无法满足需要了. 而 Map 集合可以保存多个键-值对(key-value), Set ...