自定义线程调度
响应式:响应式编程、全异步、消息、事件回调
默认还是当前线程,生成整个流、发布流、订阅流、流操作
Flux.range(1,10)
.publishOn(Schedulers.single()) //在哪个线程池就把这个流的数据和操作执行了
.log()
.subscribe();
// publishOn:改变发布者所在线程池
// subscribeOn:改变订阅者所在的线程池
public void thread1(){
Scheduler s = Schedulers.newParallel("parallel-scheduler",4);
final Flux<String> flux = Flux
.range(1,2)
.map(i -> i + 10)
.log()
.publishOn(s)
.map(i -> "value" + i);
// 只要不指定线程池,默认发布者用的线程就是订阅者的线程;
new Thread(()->flux.subscribe(System.out::println)).start();
}
// flatMap扁平化
void flatMap(){
Flux.just("zhang san","li si")
.flatMap(v->{
String[] s = v.split(" ");
return Flux.fromArray(s);//把数据包装成多元素流
})
.log() //打日志
.subscribe();// 两个人的名字,按照空格拆分,打印出所有的姓与名
}
concatMap:一个元素可以 变很多单个
流拼接操作,对于元素类型无限制
concatWith:老流拼接新的流,连接的流和老流中元素的类型相同
merge是合并,concat是拼接追加,zip流进行结对操作(编程元组的类型,最多支持8流压缩)
错误处理
默认:subscribe消费者可以感知 正常元素try与流发生的错误catch
错误是一种中断行为
//1、Catch and return a static default value捕获异常返回一个静态默认值
try {
return doSomethingDangerous(10);
}
catch (Throwable error) {
return "RECOUVERED";
}
//onErrorReturn()实现上面效果,错误的时候返回一个值
// 1、吃掉异常,消费者无异常感知
// 2、返回一个默认值
// 3、流正常完成
//2、Catch and execute an alternative path with a fallback method.
// 吃掉异常,执行一个兜底方法:
try {
return doSomethingDangerous(10);
}
catch (Throwable error) {
return doOtherthing(10);
}
// onErrorResume()
//1、吃掉一场,消费者无异常感知
//2、调用一个兜底方法
//3、流正常完成
//3、Catch and dynamically compute a fallback value.捕获并动态计算一个返回值
// 根据错误返回一个新值
try {
Value v = erroringMethod();
return MyWrapper.fromValue(v);
}
catch (Throwable error) {
return MyWrapper.fromError(error);
}
// 4、Catch, wrap to a BusinessException,and re-throw捕获并包装成一个业务异常,并重新抛出
try {
return callExternalService(k);
}
catch (throwable error) {
throw new BusinessException("opps, SLA exceeded", error)
}
// 包装重新抛出异常,推荐用onErrorMap(err->new BusinessException(err.getMessage()))
// 1、吃掉异常,消费者有感知
// 2、抛新异常
// 3、流异常完成
// 5、Catch,log an error-specific message, and re-throw捕获异常,记录特殊的错误,重新抛出
具体案例看使用手册
// 异常被捕获,做自己的事情
// 不影响异常继续顺着流水线传播
// 1、不吃掉异常,只在异常发生的时候做一件事,消费者有感知
超时与重试
timeout与retry
工具sinks
/*
Sinks.many(); 发送Flux数据
Sinks.one(); 发送Mono数据
Sinks:接收器,数据管道,所有数据顺着这个管道往下走
*/
Sinks.many().unicast();//单播,这个管道只能绑定单个订阅者(消费者)
Sinks.many().multicast();//多播,这个管道能绑定多个订阅者
Sinks.many().replay();//重放,这个管道能重返元素,是否给后来的订阅者把之前的元素依然发给它
//从头消费还是从订阅的那一刻消费
Sinks.Many<Object> many = Sinks.many()
.multicast() //多播
.onBackpressureBuffer(); //背压队列
// 默认订阅者,从订阅的那一刻开始接元素
// 发布这数据重放,底层利用队列进行缓存之前数据
// Sinks.Many<Object> many = Sinks.many().replay().limit(3);
new Thread(()->{
for (int i = 0; i < 10; i++) {
many.tryEmitNext("a" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start
//订阅
many.asFlux().subscribe(v-> System.out.println("v1= " + v));
new Thread(()->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
many.asFlux().subscribe(v-> System.out.println("v2 = " + v));
}).start();
缓存
.cache()
使用案例使用手册有,较为简单
List<Integer> integers = Flux.just(1,2,4)
.map(i -> i + 10)
.collectList()
.block(); // 也是一种订阅者,阻塞式API
sout(integers)
void parallelFlux() {
// 百万数据,8个线程,每个线程处理100,进行分批处理一直处理结束
Flux.range(1,100)
.buffer(10)
.parallel(8) // 8个并发线程
.runOn(Schedulers.newParaller("yy"))
.log()
.subscribe(v-> System.out.println("v = " + v)));
}
// 支持Context的中间操作
Flux.just(1,2,3)
.transformDeferredContextual((flux, context)->{
System.out.println("flux = " + flux);
System.out.println("context = " + context);
return flux.map(i->i+"==>"+context.get("prefix"));
})
// 上游能拿到下游的最近一次数据
.contextWrite(Context.of("prefix","哈哈"))
// ThreadLocal共享了数据,上有的所有人能看到,Context由下游传播给上游
.subscribe(v -> sout())
// 以前命令式编程
// controller -- service -- dao
// 响应式编程 dao(10:数据源)-- service(10) -- controller(10);从下游反向传播