一、响应式流接口定义:
Publisher,Subscriber,Subscription 和 Processor。
Publisher 接口声明了一个 subscribe() 方法:
public interface Publisher<T> {
void subscribe(Subscriber<? super T> subscriber);
}
Subscriber 如果订阅了,就可以从 Publisher 中接收消息。
Subscriber:
public interface Subscriber<T> {
// Subscriber 通过调用 onSubscribe() 函数将会收到第一个消息。
// 它通过一个 Subscription 对象将消息传输给 Subscriber
void onSubscribe(Subscription s);
// 每一个 Publisher 发布的项目都会通过调用 onNext() 方法,将数据传输到 Subscriber。
void onNext(T t);
// 如果出现错误,onError() 方法将被调用
void onError(Throwable t);
// 如果 Publisher 没有更多的数据需要发送了,同时也不会再生产任何数据了,
// 将会调用 onComplete()
void onComplete();
}
Subscription:
public interface Subscription {
//去请求被被发送了的数据,long 值的参数来表示它将会接收多少数据
void request(long n);
// 取消订阅
void cancel();
}
Processor接口:
// Processor 将会接收数据然后以一定的方式处理这些数据。
// 然后变为一个 Publisher,将处理的结果发布到 Subscriber。
public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {}
二、、reactor
Reactor 的两个核心类型:Mono,Flux。
Flux 表示零个、一个或多个(可能是无限个)数据项的管道
Mono 特定用于已知的数据返回项不多于一个的响应式类型
// Mono 特定用于已知的数据返回项不多于一个的响应式类型
Mono.just("Craig")
.map(n -> n.toUpperCase())
.map(cn -> "Hello, " + cn + "!")
.subscribe(System.out::println);
1)添加依赖
在pom添加Reactor依赖:
<dependency>
<!-- 在springboot中没有必要指定依赖<version> -->
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
测试依赖pom:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
非springboot项目添加依赖,需要在构建中增加 Reactor 的 Bismuth-RELEASE 。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>Bismuth-RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2)响应式操作
创建操作
联合操作
传输操作
逻辑处理操作
下面一些测试代码,在pom先引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
1.从对象进行创建
@SpringBootTest
public class ReactorTest {
// 但它没有订阅者。要是没有订阅者,数据不会流动。
@Test
public void createAFlux_just() {
//创建了一个 Flux
Flux<String> fruitFlux = Flux
.just("Apple", "Watermelon", "Banana", "Orange");
}
}
想要流动,可以添加订阅者
@Test
public void createAFlux_just() {
Flux<String> flux = Flux
.just("Apple", "Watermelon", "Banana", "Orange");
// 添加一个订阅者,可以调用 Flux 中的 subscribe() 方法:
flux.subscribe(
f -> System.out.println("fruits: " + f)
);
}
使用 StepVerifier 编写测试用例:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_just() {
Flux<String> flux = Flux
.just("Apple", "Watermelon", "Banana", "Orange");
// flux.subscribe(
// f -> System.out.println("fruits: " + f)
// );
StepVerifier.create(flux)
.expectNext("Apple")
.expectNext("Watermelon")
.expectNext("Banana")
.expectNext("Orange")
.verifyComplete();
}
}
2.从集合创建
从数组创建一个 Flux:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_fromArray() {
// 数组创建一个 Flux
String[] fruits = new String[] {
"Apple", "Watermelon", "Banana", "Orange"};
Flux<String> fFlux = Flux.fromArray(fruits);
StepVerifier.create(flux)
.expectNext("Apple")
.expectNext("Watermelon")
.expectNext("Banana")
.expectNext("Orange")
.verifyComplete();
}
}
从java.util.List、java.util.Set 或任何实现了 java.lang.Iterable 接口的类作为Flux源:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_fromIterable() {
// java.util.List、java.util.Set 或任何实现了 java.lang.Iterable 接口的类
List<String> fruitList = new ArrayList<>();
fruitList.add("Apple");
fruitList.add("Watermelon");
fruitList.add("Banana");
fruitList.add("Orange");
Flux<String> flux = Flux.fromIterable(fruitList);
StepVerifier.create(flux)
.expectNext("Apple")
.expectNext("Watermelon")
.expectNext("Banana")
.expectNext("Orange")
.verifyComplete();
}
}
Java Stream 作为 Flux 源:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_fromStream() {
Stream<String> fruitStream =
Stream.of("Apple", "Watermelon", "Banana", "Orange");
Flux<String> flux = Flux.fromStream(fruitStream);
// ... verify steps
StepVerifier.create(flux)
.expectNext("Apple")
.expectNext("Watermelon")
.expectNext("Banana")
.expectNext("Orange")
.verifyComplete();
}
}
3.生成 Flux 数据
创建一个范围的 Flux:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_range() {
//创建的范围 Flux 的起始值为 1,结束值为 5
Flux<Integer> flux = Flux.range(1, 5);
StepVerifier.create(flux)
.expectNext(1)
.expectNext(2)
.expectNext(3)
.expectNext(4)
.expectNext(5)
.verifyComplete();
}
}
静态的 interval() 方法来创建每秒发送一个值的 Flux:
@SpringBootTest
public class ReactorTest {
@Test
public void createAFlux_interval() {
// 间隔 Flux 发出的值以 0 开始,并在每个连续项上递增
Flux<Long> flux = Flux.interval(Duration.ofSeconds(1)).take(5);
StepVerifier.create(flux)
.expectNext(0L)
.expectNext(1L)
.expectNext(2L)
.expectNext(3L)
.expectNext(4L)
.verifyComplete();
}
}
三、响应式类型结合
1、合并响应式类型
如果两个 Flux 流要建立一个汇聚结果的 Flux,可以使用mergeWith()。
@SpringBootTest
public class ReactorTest {
@Test
public void mergeFluxes() {
Flux<String> flux1 = Flux
.just("a", "b", "c")
// 通常情况下,Flux 会尽可能快的快地发送数据.
// 使用delayElements() 操作,用来将数据发送速度减慢 —— 每 0.5s 发送一个数据。
.delayElements(Duration.ofMillis(500));
Flux<String> flux2 = Flux
.just("d", "e", "f")
// 在延迟 250ms 后才会发送数据
.delaySubscription(Duration.ofMillis(250))
.delayElements(Duration.ofMillis(500));
// mergeWith() 不能保证源之间的完美交替
Flux<String> mergedFlux = flux1.mergeWith(flux2);
// 验证
StepVerifier.create(mergedFlux)
.expectNext("a")
.expectNext("d")
.expectNext("b")
.expectNext("e")
.expectNext("c")
.expectNext("f")
.verifyComplete();
}
}
把 flux1 和 flux2 压缩在一起:
@SpringBootTest
public class ReactorTest {
@Test
public void zipFluxes() {
Flux<String> flux1 = Flux.just("a", "b", "c");
Flux<String> flux2 = Flux.just("d", "e", "f");
// 与 mergeWith() 不同的是,zip() 操作是一个静态的创建操作。
// 从压缩后的 Flux 发送出来的每个项目都是 Tuple2(包含两个对象的容器)。
Flux<Tuple2<String, String>> zippedFlux = Flux.zip(flux1, flux2);
StepVerifier.create(zippedFlux)
.expectNextMatches(p ->
p.getT1().equals("a") &&
p.getT2().equals("d"))
.expectNextMatches(p ->
p.getT1().equals("b") &&
p.getT2().equals("e"))
.expectNextMatches(p ->
p.getT1().equals("c") &&
p.getT2().equals("f"))
.verifyComplete();
}
}
何压缩的 flux1 和 flux2,产生 String 类型的的 Flux 对象:
@SpringBootTest
public class ReactorTest {
@Test
public void zipFluxesToObject() {
Flux<String> flux1 = Flux.just("a", "b", "c");
Flux<String> flux2 = Flux.just("d", "e", "f");
// 给 zip() 的 Function 接口(这里给出一个 lambda 表达式)把两个值连接,由压缩后的 Flux 进行数据发送
Flux<String> zippedFlux = Flux.zip(flux1, flux2,
(c, f) -> c + " ---> " + f);
StepVerifier.create(zippedFlux)
.expectNext("a ---> d")
.expectNext("b ---> e")
.expectNext("c ---> f")
.verifyComplete();
}
}
2、选择第一个响应式类型进行发布
@SpringBootTest
public class ReactorTest {
@Test
public void firstFlux() {
// 因为在flux2 已经开始发布后 100ms,flux1 才开始发布数据。
// 所以新创建的 Flux 将完全忽略flux1,而只发布flux2中的数据
Flux<String> flux1 = Flux.just("a", "b", "c")
.delaySubscription(Duration.ofMillis(100));
Flux<String> flux2 = Flux.just("d", "e", "f");
// 通过使用 first(),它创建了一个新的 Flux,
// 只发布从第一个源 flux2 发布的数据
Flux<String> firstFlux = Flux.first(flux1, flux2);
StepVerifier.create(firstFlux)
.expectNext("d")
.expectNext("e")
.expectNext("f")
.verifyComplete();
}
}
四、转换和过滤响应式流
1、从响应式类型中过滤数据
跳过指定数量的项:
// 跳过指定数量的项
@Test
public void skipSome() {
// skip(3) 将生成一个新的流,该流跳过前三个项,并且只发布最后两个项
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "e")
.skip(3);
StepVerifier.create(flux1)
.expectNext("d", "e")
.verifyComplete();
}
过一段时间再跳过前几个项目(skip):
@Test
public void skipSeconds() {
// 在发出任何值之前等待 4 秒的 Flux,项之间具有 1 秒延迟
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "e")
.delayElements(Duration.ofSeconds(1))
.skip(Duration.ofSeconds(4)); // skip() 跳过前几个项
StepVerifier.create(flux1)
.expectNext("d", "e")
.verifyComplete();
}
只发出前几个项(take):
@Test
public void take() {
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "e")
.take(3);//take() 只发出前几个项
StepVerifier.create(flux1)
.expectNext("a", "b", "c")
.verifyComplete();
}
使用 take() 的在订阅后的前 n(这里是3.5秒) 秒内发出尽可能多的项:
@Test
public void take2() {
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "e")
.delayElements(Duration.ofSeconds(1))
.take(Duration.ofMillis(3500));
StepVerifier.create(flux1)
.expectNext("a", "b", "c")
.verifyComplete();
}
skip() 和 take() 操作可以看作是基于计数或持续时间的筛选条件的操作。
还有个filter():
给定一个决定一个项是否通过 Flux 的 Predicate,filter() 操作允许你根据需要的任何条件有选择地发布。
@Test
public void filter() {
// filter() 被赋予一个 Predicate。
// 只接收没有空格的String。
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "hello world", "e")
.filter(np -> !np.contains(" "));
StepVerifier.create(flux1)
.expectNext("a", "b", "c", "d", "e")
.verifyComplete();
}
过滤的是已经收到的任何项目,如只有唯一的 String 值将从 Flux 中发出:
@Test
public void distinct() {
// 尽管 “a” 从源 Flux 中发布两次,但在 distinct Flux 中只发布一次
Flux<String> flux1 = Flux.just(
"a", "b", "c", "a", "e", "d")
.distinct(); // 只有唯一的 String 值将从 Flux
StepVerifier.create(flux1)
.expectNext("a", "b", "c", "e", "d")
.verifyComplete();
}
2、映射响应式数据
用户的 String 值的 Flux 映射到 User 对象的新 Flux,User为自定义的用户类。
// map() 映射是同步执行的。 因为每个项都是由源 Flux 发布的.
// 如果要异步执行映射,应使用 flatMap() 操作
@Test
public void mapdemo() {
// 用 just() 创建的流携带的是 String 对象
// 但由 map() 生成的流携带的是 User 对象
Flux<User> flux1 = Flux
.just("zhang san", "wang wu", "zhao liu")
.map(n -> {
String[] split = n.split("\\s");
return new User(split[0], split[1]);
});
StepVerifier.create(flux1)
.expectNext(new User("zhang", "san"))
.expectNext(new User("wang", "wu"))
.expectNext(new User("zhao", "liu"))
.verifyComplete();
}
flatMap与subscribeOn方法一起使用:(可以通过将工作分成多个并行线程来增加流的吞吐量)
@Test
public void flatMapDemo() {
Flux<User> flux1 = Flux
.just("zhang san", "wang wu", "zhao liu")
.flatMap(n -> Mono.just(n).map(p -> { // 将传入 String 转换为 String 类型的 Mono
String[] split = p.split("\\s");
return new User(split[0], split[1]); // 将 String 转换为 User
})
.subscribeOn(Schedulers.parallel()));// subscribeOn表示每个订阅应该在一个并行线程中进行。
List<User> userList = Arrays.asList(
new User("zhang", "san"),
new User("wang", "wu"),
new User("zhao", "liu"));
//因为是并行完成的,所以无法保证先完成哪项,无法知道产生的 Flux 中排放的项目的顺序。
// StepVerifier 只能验证发出的每个项是否存在于 User 对象的预期列表中,
//并且在 Flux 完成之前将有三个这样的项
StepVerifier.create(flux1)
.expectNextMatches(p -> userList.contains(p))
.expectNextMatches(p -> userList.contains(p))
.expectNextMatches(p -> userList.contains(p))
.verifyComplete();
}
调度程序支持多个并发模型:
Schedulers 方法 | 描述 |
---|---|
.immediate() | 在当前线程中执行订阅 |
.single() | 在单个可重用线程中执行订阅,对所有调用方重复使用同一线程 |
.newSingle() | 在每个调用专用线程中执行订阅 |
.elastic() | 在从无限弹性池中提取的工作进程中执行订阅,根据需要创建新的工作线程,并释放空闲的工作线程(默认情况下 60 秒) |
.parallel() | 在从固定大小的池中提取的工作进程中执行订阅,该池的大小取决于 CPU 核心的数量。 |
3、在响应式流上缓冲数据
@Test
public void buffer() {
// 创建一个新的 List 集合的 Flux,其中每个 List 的元素数不超过指定的数目
Flux<String> flux1 = Flux.just(
"a", "b", "c", "d", "e");
// String 元素的 Flux 被缓冲到一个 List 集合的新 Flux 中,
// 每个 List 集合包含的项不超过三个。
Flux<List<String>> bufferedFlux = flux1.buffer(3);
StepVerifier
.create(bufferedFlux)
.expectNext(Arrays.asList("a", "b", "c"))
.expectNext(Arrays.asList("d", "e"))
.verifyComplete();
}
buffer() 与 flatMap() 结合使用,可以并行处理每个 List 集合:
@Test
public void bufferAndFlatMap() {
Flux.just(
"a", "b", "c", "d", "e")
.buffer(3)
.flatMap(x ->
Flux.fromIterable(x)
.map(y -> y.toUpperCase())
.subscribeOn(Schedulers.parallel())
.log()
).subscribe();
}
collectList() 产生一个 Mono:
@Test
public void collectListDemo() {
Flux<String> flux = Flux.just(
"a", "b", "c", "d", "e");
//collectList() 生成一个发布 List 的 Mono,而不是生成一个发布 List 的 Mono
Mono<List<String>> mono = flux.collectList();
StepVerifier
.create(mono)
.expectNext(Arrays.asList(
"a", "b", "c", "d", "e"))
.verifyComplete();
}
键又第一个字母确定,经流的最后一个条目将覆盖所有先前的条目。
@Test
public void collectMap() {
Flux<String> flux = Flux.just(
"a2", "b2", "k2", "b1", "a5", "p3");
Mono<Map<Character, String>> mono =
flux.collectMap(a -> a.charAt(0));
mono.subscribe(
f -> System.out.println("fruits: " + f)
);
StepVerifier
.create(mono)
.expectNextMatches(map -> {
return
map.size() == 4 &&
map.get('a').equals("a5") &&
map.get('k').equals("k2") &&
map.get('b').equals("b1") &&
map.get('p').equals("p3");
})
.verifyComplete();
}
五、逻辑操作
有时只要是否符合某些条件,all()
和 any()
操作可以执行这样的逻辑。
all():
@Test
public void allDemo() {
Flux<String> flux = Flux.just(
"ab", "asd", "aqwer", "abcd");
// 所有的条目都包含字母 a,false
Mono<Boolean> hasAMono = flux.all(a -> a.contains("a"));
StepVerifier.create(hasAMono)
.expectNext(true)
.verifyComplete();
// 所有的条目都包含字母 b,false
Mono<Boolean> hasBMono = flux.all(a -> a.contains("b"));
StepVerifier.create(hasBMono)
.expectNext(false)
.verifyComplete();
}
至少有一个条目匹配:
any():
@Test
public void any() {
Flux<String> flux = Flux.just(
"ab", "asd", "aqwer", "abcd");
// 至少有一个条目包含a, true
Mono<Boolean> hasSMono = flux.any(a -> a.contains("s"));
StepVerifier.create(hasSMono)
.expectNext(true)
.verifyComplete();
// 至少有一个条目包含n,false
Mono<Boolean> hasNMono = flux.any(a -> a.contains("n"));
StepVerifier.create(hasNMono)
.expectNext(false)
.verifyComplete();
}