响应式编程概念:
(1)响应式编程=数据流+变化传递+声明式
响应式编程 是一种不同于Servlet的全新的编程范式和技术栈,它基于异步非阻塞的特性 ,能够借助EventLoop以少量线程应对高并发的访问,对微服务架构也颇有助益。
异步非阻塞并不会使程序运行得更快。WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。Spring WebFlux 是一个异步非阻塞的 Web 框架,所以,它特别适合应用在 IO 密集型的服务中。
(2)在Java 9版本中,响应式流的规范被纳入到了JDK中,相应的API接口是java.util.concurrent.Flow。
(3)Spring推出的响应式模块Spring-WebFlux,首选Reactor作为其响应式技术栈的一部分。
(4)在响应式流中,数据流的发出者(发布者)叫做Publisher ,监听者(订阅者)叫做Subscriber。订阅者处理完一个元素的时候通过request(1)跟发布者再请求一个元素
(5)流量控制------回压
1)假如发布者发出数据的速度和订阅者处理数据的速度不同的时候,如果没有流量控制,那么订阅者会被发布者快速产生的数据流淹没。
2)订阅者需要有一种能够向上游反馈流量需求的机制:回压(背压)
3)回压的处理会涉及不同的策略:
3-1)缓存的策略:处理方式与消息队列有些相似之处,发布者需要维护一个队列用来缓存还没有被处理的元素。通常用于对数据准确性要求比较高的场景,比如发布者这儿是突然到来的数据高峰,都是要保存到数据库的,作为订阅者没有那么快的处理速度,那么发布者就需要将数据暂时缓存起来。
3-2)丢弃的策略:发布者不需要缓存来不及处理的数据,而是直接丢弃,当订阅者请求数据的时候,会拿到发布者那里最近的一个数据元素。
在Reactor 3中如何通过generate和create等方法生成自定义的数据流,是命令式编程与响应式编程的桥梁。
Reactor API
Reactor 提供了实现 Publisher 的响应式类 Flux 和 Mono,以及丰富的操作符。一个Flux 代表 0...N 个元素的响应式流 ;一个Mono 代表 0|1 个元素的响应式流 (Flux的方法它大多也都能用)。
Flux 和 Mono 之间可以转换,比如 Flux 的 count 操作(计算流中元素个数)返回 Mono,Mono 的 concatWith 操作(连接另一个响应式流)返回 Flux。
一、Flux & Mono(Publisher对象)
1****.**** ****Flux<T> 是一个能够发出 0 到 N 个元素的标准 Publisher<****T>,它会被一个完成(completion)或错误(error)信号终止。因此,一个 Flux 的可能结果是 value、completion
或 error,这三个分别会传递给订阅者中的 onNext、onComplete、onError 方法。
注意:所有的信号事件,包括代表终止的信号事件都是可选的。 如果没有 onNext 事件,但是有 onComplete 事件,那么发出的就是空的有限流;如果去掉 onComplete
就得到一个 无限的空数据流。无限的数据流可以不是空的,比如 Flux.interval(Duration) 生成的是一个 Flux,这是一个无限周期性发出规律整数的时钟数据流。
2.Mono<T> 是一种特殊的Publisher<T>(Flux的方法它大多也都能用) ,它最多只能发出一个元素,然后(可选的)终止于 onComplete 或 onError 信号。
Mono 中的操作符是 Flux 中操作符的子集,即 Flux 中只有部分操作符适用于 Mono,有些操作符是将 Mono 和另一个 Publisher 连接转换为 Flux。例如,Mono#concatWith(Publisher) 转换为 Flux,Mono#then(Mono) 返回另一个 Mono。
注意:可以使用 Mono 来创建一个只有完成概念的空值异步处理过程(类似于 Runnable)。
二、构建Flux&Mono 基本api:
1.just (Flux/Mono) : 使用提供的元素发出数据然后结束的流。
Flux<Integer> flux = Flux.just(1, 2, 3, 4, 5); flux.subscribe( value -> System.out.println("Received: " + value), error -> System.err.println("Error: " + error), () -> System.out.println("Completed") );
Mono<Integer> mono = Mono.just(1); mono.subscribe( value -> System.out.println("Received: " + value), error -> System.err.println("Error: " + error), () -> System.out.println("Completed") );
just 接收的是一个可变长参数,其实生成的就是 5 个整数的元素序列 。就是publisher
subscribe方法是订阅的 意思也就是消费元素 的下游方法。就是Subscriber
2.Flux#fromXxx
Flux 提供了 fromArray(从数组)、fromIterable(从迭代器)、fromStream(从 Java Stream 流) 的方式来创建 Flux。
String[] array = new String[]{"hello", "reactor", "flux"};
List<String> iterable = Arrays.asList("foo", "bar", "foobar"); Flux.fromArray(array).subscribe(System.out::println); Flux.fromIterable(iterable).subscribe(System.out::println); Flux.fromStream(Arrays.stream(array)).subscribe(System.out::println);
3.Flux#range
从 start 开始构建一个 Flux,该 Flux 仅发出一系列递增计数的整数。 也就是说,在 start(包括)和 start + count(排除)之间发出整数,然后完成
Flux.range(3, 5).subscribe(System.out::println);
4.Flux#interval
interval 生成的是一个无限数据流。
在全局计时器上创建一个 Flux,该 Flux 在初始延迟后,发出从0开始并以指定的时间间隔递增的长整数。 如果未及时产生,则会通过溢出 IllegalStateException 发出 onError信号,详细说明无法发出的原因。 在正常情况下,Flux 将永远不会完成。interval 提供了 3 个重载方法,三者的区别主要在于是否延迟发出、以及使用的调度器。
Flux<Long> interval(Duration period)
没有延迟,按照 period 的周期立即发出,默认使用 Schedulers.parallel() 调度器
Flux<Long> interval(Duration delay, Duration period)
以 delay 延迟,按照 period 的周期发出,默认使用 Schedulers.parallel() 调度器
Flux<Long> interval(Duration delay, Duration period, Scheduler timer)
以 delay 延迟,按照 period 的周期发出,使用指定的调度器
Flux.interval(Duration.ofMillis(30), Duration.ofMillis(500)).subscribe(System.out::println);
5.Flux# create
Flux.create(emitter->{
emitter.next(1); emitter.next(2); emitter.complete();
}).subscribe(System.out::println);
注意:Flux这里的next 可以调用多次( Flux发出 0 到 N 个元素)。
Mono.create(monoSink -> {
monoSink. success(1);
}).subscribe(System.out::println);
注意:Mono这里创建元素且只能创建一个。且没有complete等方法(Mono 0或者1个元素)。
6.Flux# error
创建一个立即抛出指定错误的 Mono
Mono.error(Throwable error)
三、Reactor 操作符(常用)
在 Reactor 中,每个操作符对 Publisher 进行处理,然后将 Publisher 包装为另一个新的 Publisher 。就像一个链条,数据源自第一个 Publisher ,然后顺链条而下,在每个环节进行相应的处理。最终,订阅者(Subscriber )终结这个过程。所以, 响应式编程按照链式方式进行开发。
如同 Java Stream 的中间操作一样,Reactor 的 Flux 和 Mono 也为我们提供了多种操作符
转换类的操作符数量最多,平常过程中也是使用最频繁的。
0.timeout 操作符: 可以在流中发生超时时发出一个默认值。
Flux.just(1, 2, 3).delayElements(Duration.ofSeconds(1))
.timeout(Duration.ofMillis(500), Mono.just(0)).subscribe(System.out::println);
1.as: 将响应式流转换为目标类型,既可以是非响应式对象,也可以是 Flux 或 Mono。
Flux.range(3, 8).as(Mono::from) .subscribe(System.out::println);
2.cast: 将响应式流内的元素强转为目标类型,如果类型不匹配(非父类类型或当前类型),将抛出 ClassCastException。
Flux.range(1, 3) .cast(Number.class) .subscribe(System.out::println);
3-1.collect
通过应用收集器,将 Flux 发出的所有元素收集到一个容器中。当此流完成时,发出收集的结果。
Flux.range(1, 5) .collect(Collectors.toList()) .subscribe(System.out::println);
3-2.collectList收集到一个列表中,当此流完成时,发出收集的结果
Flux.range(1, 5) .collectList() .subscribe(System.out::println);
4.collectMap 收集到map中,当此流完成时,发出收集的结果
Flux.just(1, 2, 3, 4, 5, 3, 1) .collectMap(n -> n, n -> n + 100) .subscribe(System.out::println);
spring-boot-starter-webflux
1.pom.xml引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.Spring WebFlux支持基于注释的响应式编程写法,与Spring MVC非常相近:
@RestController
@RequestMapping("/users")
public class MyRestController {
@GetMapping("/{user}")
public Mono<User> getUser(@PathVariable Long user) {
// ...
}
@GetMapping("/{user}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long user) {
// ...
}
@DeleteMapping("/{user}")
public Mono<User> deleteUser(@PathVariable Long user) {
// ...
}
}
3.实战demo案例:
@PostMapping("/ctrl/{modelApiCode}")
public Mono<Result> ctrl(@PathVariable String modelApiCode, @RequestBody ProtocolModelApiInvokeParam param) {
Object[] objects = this.getProtocolModelApi(modelApiCode, param);
DeviceProtocolSupplier supplier = (DeviceProtocolSupplier) objects[3];
//返回一个Mono对象
return Mono.<InvokeResult>create(item -> { //Mono.create创建一个Publisher发布者
try {
RealtimeStatus realtimeStatus = cacheManager.get(supplier.getProtocol().getCode(), param.buildKey());
if(realtimeStatus.getStatus() == DeviceStatus.offline) {
if(realtimeStatus.getType() == DeviceType.Direct) {
//item.success固定语法: Publisher发出元素
item.success(InvokeResult.fail("设备不在线["+realtimeStatus.getDeviceName()+"]", null)); return;
}
}
ProtocolInvokeUtil.invoke(supplier, (ModelApi) objects[1], param, realtimeStatus, result -> {
//item.success固定语法: Publisher发出元素
item.success(result);
// 将实时结果写出到前端
IotThreadManager.instance().getExecutorService().execute(() -> {
realtimeListener.push(param.getUid(), (ModelApi) objects[1], result);
});
});
} catch (ProtocolInvokeException e) {
e.printStackTrace();
//item.success固定语法: Publisher发出元素
item.success(InvokeResult.fail(e.getMessage(), null));
}
}).timeout(Duration.ofSeconds(10), Mono.error(new ServiceException("执行超时")))//timeout超时 则抛出异常
.map(item -> {//Reactor 操作符map,对Mono.create中item.success()发出的元素进行map转换处理
if(item.getStatus() == ExecStatus.success) {
return Result.success(item.getValue());//返回Result对象,展示到前端
} else {
return Result.fail(item.getReason());//返回Result对象,展示到前端
}
});
}