工厂方法创建流

  • Backpressure : the ability for the consumer to signal the producer that the rate of emission is too high

push工厂方法

通过单线程生产者(在同一时间只有一个线程,可以调用next,complete或error)创建Flux实例,此方法适配于异步,单线程,多值Api,无须关注背压和取消。

同样可以桥接接口,示例见create示例(把create换成push)

  • push()并且create()两者都允许设置onRequest使用者以管理请求量并确保仅在有待处理的请求时才通过接收器推送数据。

  • onCancel 首先调用,仅用于取消信号。

  • onDispose 为完成,错误或取消信号而调用。

package com.ccand99.projectreactor.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux; import java.time.Duration;
import java.util.stream.IntStream; public class pushDemo { private static final Logger log = LoggerFactory.getLogger(pushDemo.class); public static void main(String[] args) throws InterruptedException {
push();
Thread.sleep(3000);
} static void push() {
//IntStream.range(2000,3000) Stream API 生成1000个整数
//emitter 为FluxSink<T>类型,next发送到Flux实例
//push 方法不需要关心背压和取消
Flux.push(emitter -> IntStream.range(2000,3000).forEach(emitter::next))
//延迟模拟背压
.delayElements(Duration.ofMillis(1))
.subscribe(e -> log.info("onNext: {}",e));
}
}

create工厂方法

从不同线程发送事件,会序列化FluxSink。和push都支持重载溢出策略,能通过注册额外的处理程序清理资源。可以支持接口桥接(官网有示例)

此外,由于create可以桥接异步API并管理背压,因此您可以通过指示以下内容来完善如何进行背压行为(backpressure)

OverflowStrategy:

IGNORE完全忽略下游背压请求。当队列下游充满时,可能会IllegalStateException。

ERRORIllegalStateException在下游无法跟上时发出信号。

DROP 如果下游没有准备好接收它,则丢弃输入的信号。

LATEST 让下游只从上游获取最新信号。

BUFFER(默认值),以在下游无法跟上时缓冲所有信号。(这会实现无限缓冲,并可能导致OutOfMemoryError)。

package com.ccand99.projectreactor.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Disposable;
import reactor.core.publisher.Flux; import java.util.List; public class CreateDemo { private static final Logger log = LoggerFactory.getLogger(CreateDemo.class); public static void main(String[] args) throws InterruptedException {
create();
Thread.sleep(1000);
} //和push都支持重载溢出策略,能通过注册额外的处理程序清理资源。
static void create() throws InterruptedException {
Disposable disposable = Flux.create(emitter -> {
emitter.onDispose(() -> log.info("Dispose"));
//将事件推送到发射器。
emitter.next("test");
}).subscribe(e -> log.info("onNext: {}", e));
Thread.sleep(500);
disposable.dispose();
} //接口桥接官网示例
interface MyEventListener<T> {
void onDataChunk(List<T> chunk);
void processComplete();
} //此类只是为了编译
static class MyEventProcessor {
private MyEventListener myEventListener; public MyEventProcessor() { } void register(MyEventListener myEventListener){
this.myEventListener = myEventListener;
} List<String> getHistory(long n){ return null; } //...
} static void officialDemo1() {
MyEventProcessor myEventProcessor = new MyEventProcessor();
Flux<String> bridge = Flux.create(sink -> {
//接口实现类桥接到Flux
myEventProcessor.register(
new MyEventListener<String>() {
public void onDataChunk(List<String> chunk) {
for(String s : chunk) {
sink.next(s);
}
} public void processComplete() {
sink.complete();
}
});
});
} //混合推拉,使用request
static void pullAndpush() {
MyEventProcessor myEventProcessor = new MyEventProcessor();
Flux<String> bridge = Flux.create(sink -> {
myEventProcessor.register(
new MyEventListener<String>() {
public void onDataChunk(List<String> chunk) {
for(String s : chunk) {
sink.next(s);
}
} public void processComplete() {
sink.complete();
}
});
sink.onRequest(n -> {
// 发出请求时轮询消息。
List<String> messages = myEventProcessor.getHistory(n);
for(String s : messages) {
//如果消息立即可用,请将其推入接收器。
sink.next(s);
}
});
});
} }

generate工厂方法

适用在基于生成器内部处理状态创建复杂序列。接收器为一个 SynchronousSink,其next()方法每次回调调用最多只能调用一次

package com.ccand99.projectreactor.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.util.function.Tuples; import java.time.Duration;
import java.util.concurrent.atomic.AtomicLong; public class GenerateDemo { private static final Logger log = LoggerFactory.getLogger(GenerateDemo.class); public static void main(String[] args) throws InterruptedException {
//generate();
//officialDemo1();
//officialDemo2();
officialDemo3();
Thread.sleep(1000);
} static void generate() {
Flux.generate(
// 初始状态
() -> Tuples.of(0L, 1L),
//这里类似reduce操作符
(state, sink) -> {
log.info("generated value {}", state.getT2());
sink.next(state.getT2());
long newValue = state.getT1() + state.getT2();
return Tuples.of(state.getT2(), newValue);
})
//同delayElements模拟会执行BiFunction,再执行onNext
//.delayElements(Duration.ofMillis(1))
.take(7)
.subscribe(e -> log.info("onNext: {}", e));
} //https://projectreactor.io/docs/core/release/reference/
static void officialDemo1() {
Flux<String> flux = Flux.generate(
() -> 0,
(state, sink) -> {
log.info("generated value {}", state);
sink.next("3 x " + state + " = " + 3 * state);
if (state == 10) sink.complete();
return state + 1;
});
flux.delayElements(Duration.ofMillis(1))
.take(7).subscribe(e -> log.info("onNext: {}", e));
} //generate(Supplier<S>, BiFunction, Consumer<S>)
static void officialDemo2() {
Flux<String> flux = Flux.generate(
//这次,我们生成一个可变对象作为状态
AtomicLong::new,
(state, sink) -> {
log.info("generated value {}", state);
long i = state.getAndIncrement();
sink.next("3 x " + i + " = " + 3*i);
if (i == 10) sink.complete();
return state;
});
flux.take(7).subscribe(e -> log.info("onNext: {}", e));
} //包含consumer
static void officialDemo3() {
Flux<String> flux = Flux.generate(
AtomicLong::new,
(state, sink) -> {
log.info("generated value {}", state);
long i = state.getAndIncrement();
sink.next("3 x " + i + " = " + 3*i);
if (i == 10) sink.complete();
return state;
}, (state) -> System.out.println("state: " + state));
flux.take(7).subscribe(e -> log.info("onNext: {}", e));
}
}

Using工厂方法

using工厂方法能根据一个disposable创建流,在响应式中实现了try-with-resources。

Flux<String> flux =
Flux.using(
() -> disposableInstance,
disposable -> Flux.just(disposable.toString()),
Disposable::dispose
);
package com.ccand99.projectreactor.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux; import java.util.Arrays;
import java.util.Random; public class UsingDemo { private static final Logger log = LoggerFactory.getLogger(UsingDemo.class); public static void main(String[] args) {
using();
} //try-with-resources方式,此方式自动关闭代码块
static void CommandModel() {
try (Connection connection = Connection.newConnection()) {
connection.getData().forEach(
data -> log.info("Received data: {}",data)
);
} catch (Exception e){
log.info("Error: {}",e.getMessage()) ;
}
} static void using() {
Flux<String> ioRequestResult = Flux.using(
//关联Connection生命周期
Connection::newConnection,
//转换方式fromIterable
connection -> Flux.fromIterable(connection.getData()),
//关闭方法
Connection::close
);
ioRequestResult.subscribe(
data -> log.info("Received data: {}",data),
e -> log.info("Error: {}",e.getMessage()),
() -> log.info("Stream finish")
);
} //包装一个阻塞API(简化的Connection)
private static class Connection implements AutoCloseable { private final Random random = new Random(); public Iterable<String> getData() {
if (random.nextInt(10) < 3) {
throw new RuntimeException("Communication error");
}
return Arrays.asList("Some","data");
} @Override
public void close() {
log.info("IO Connection closed");
} public static Connection newConnection() {
log.info("IO Connection created");
return new Connection();
}
}
}

usingWhen工厂

包装响应式事务。using通过Callable实例获取资源,usingWhen通过订阅Publisher。

package com.ccand99.projectreactor.factory;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import java.time.Duration;
import java.util.Random;
import java.util.function.BiFunction; public class UsingWhenDemo { private static final Logger log = LoggerFactory.getLogger(UsingWhenDemo.class); public static void main(String[] args) throws InterruptedException { Flux.usingWhen(
Transaction.beginTransaction(),
transaction -> transaction.insertRows(Flux.just("A", "B", "C")),
Transaction::commit,
new BiFunction<Transaction, Throwable, Publisher<?>>() {
@Override
public Publisher<?> apply(Transaction transaction, Throwable throwable) {
transaction.rollback();
log.info("error {}",throwable.getMessage());
return Mono.empty();
}
},
Transaction::rollback
).subscribe(
d -> log.info("onNext: {}",d),
e -> log.info("onError: {}",e),
() -> log.info("onComplete")
);
Thread.sleep(1000);
} }
//简化的事务处理模型
class Transaction { private static final Logger log = LoggerFactory.getLogger(Transaction.class); private static final Random random = new Random(); private final int id; public Transaction(int id) {
this.id = id;
log.info("[T: {}] created",id);
} //静态方法,生成事务
public static Mono<Transaction> beginTransaction() {
return Mono.defer(
() -> Mono.just(new Transaction(random.nextInt(1000)))
);
} public Flux<String> insertRows(Publisher<String> rows) {
//模拟插入,利用随机模拟产生插入失败的行为。
return Flux.from(rows)
.delayElements(Duration.ofMillis(100))
.flatMap( r-> {
if (random.nextInt(10) < 2) {
return Mono.error(new RuntimeException("Error: "+ r) );
} else {
return Mono.just(r);
}
});
} //异步提交,有时提交失败
public Mono<Void> commit() {
return Mono.defer( () -> {
log.info("[T: {}] commit",id);
if (random.nextBoolean()) {
return Mono.empty();
} else {
return Mono.error(new RuntimeException("conflict"));
}
});
} //异步回滚,有时事务回滚失败
public Mono<Void> rollback() {
return Mono.defer( () -> {
log.info("[T: {}] rollback",id);
if (random.nextBoolean()) {
return Mono.empty();
} else {
return Mono.error(new RuntimeException("conflict"));
}
});
}
}

错误处理

Demo:

package com.ccand99.projectreactor.handleError;

import reactor.core.publisher.Flux;
import reactor.util.retry.Retry; import java.time.Duration;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; public class BadDemo { public static void main(String[] args) throws InterruptedException {
handle();
Thread.sleep(8000);
} static void handle() {
Flux.just("user-1")
.flatMap(user ->
recommendedBooks(user).retryWhen(Retry.backoff(5, Duration.ofMillis(100)))
.timeout(Duration.ofSeconds(4))
.onErrorResume(e -> Flux.just("The Martian"))
)
.subscribe(System.out::println,System.out::println,System.out::println);
} static Flux<String> recommendedBooks(String userId) {
return Flux.defer(
() -> {
if (new Random().nextInt(10) < 7) {
return Flux.<String>error(new RuntimeException("err"))
.delaySequence(Duration.ofMillis(100));
} else {
return Flux.just("Blue Mars", "The Expanse")
.delayElements(Duration.ofMillis(50));
}
}).doOnSubscribe(s -> System.out.println(userId));
} //官网retry示例:
static void offcial() {
AtomicInteger errorCount = new AtomicInteger();
AtomicInteger transientHelper = new AtomicInteger();
Flux<Integer> transientFlux = Flux.<Integer>generate(sink -> {
int i = transientHelper.getAndIncrement();
if (i == 10) {
// 我们generate的消息源中含有大量错误。当计数器达到10时,它将成功完成
sink.next(i);
sink.complete();
}
else if (i % 3 == 0) {
// 如果transientHelper原子是的倍数3,我们将发射onNext并结束当前的脉冲串。
sink.next(i);
}
else {
// 在其他情况下,我们发出onError。那是三分之二,所以2的爆发onError被1中断onNext
sink.error(new IllegalStateException("Transient error at " + i));
}
})
.doOnError(e -> errorCount.incrementAndGet()); //如果没有transientErrors(true),2第二个脉冲串将达到配置的最大尝试次数,并且发出该序列后将失败onNext(3)。
transientFlux.retryWhen(Retry.max(2).transientErrors(true))
.blockLast();
//assertThat(errorCount).hasValue(6);
}
}

反应序列中的 任何错误都是终端事件。即使使用了错误处理运算符,它也不会让原始序列继续。相反,它将onError信号转换为新序列的开始(后备序列)。换句话说,它将替换其上游的终止序列。未经检查的异常总是通过传播onError.

错误处理运算符决策树

  1. 在subscribe中的onError定义处理程序(如果未定义,则onError抛出UnsupportedOperationException。您可以使用Exceptions.isErrorCallbackNotImplemented方法进一步对其进行检测和分类。)

  2. 通过onErrorReturn来捕获错误,用一个默认值或从异常出计算出的值替换,等效于try-catch

    Flux.just(1, 2, 0)
    .map(i -> "100 / " + i + " = " + (100 / i)) //this triggers an error with 0
    .onErrorReturn("Divided by zero :("); // error handling example
  3. 可以用onErrorResume捕获异常,并执行备用工作流

    //如果您的标称进程正在从外部且不可靠的服务中获取数据,但是您还保留了同一数据的本地缓存,该缓存可能会过时但更可靠
    Flux.just("key1", "key2")
    .flatMap(k -> callExternalService(k)
    .onErrorResume(e -> getFromCache(k)));

    等效于

    String v1;
    try {
    v1 = callExternalService("key1");
    }
    catch (Throwable error) {
    v1 = getFromCache("key1");
    } String v2;
    try {
    v2 = callExternalService("key2");
    }
    catch (Throwable error) {
    v2 = getFromCache("key2");
    }

    如果本是一个带异常处理的Futrue:

    erroringFlux.onErrorResume(error -> Mono.just(
    MyWrapper.fromError(error)
    ));
  4. 用onErrorMap转换为另一个异常处理

    Flux.just("timeout1")
    .flatMap(k -> callExternalService(k))
    .onErrorMap(original -> new BusinessException("oops, SLA exceeded", original));
  5. doOnError 运算符,等效于“捕获,记录特定于错误的消息并重新抛出”模式

    LongAdder failureStat = new LongAdder();
    Flux<String> flux =
    Flux.just("unknown")
    .flatMap(k -> callExternalService(k)
    .doOnError(e -> {
    failureStat.increment();
    log("uh oh, falling back, service failed for key " + k);
    }) );
  6. doFinally和using doFinally与序列终止(用onComplete或onError或取消)时要执行的副作用有关。它提示您哪种终止方式会引起副作用。using请查看工厂说明

    Stats stats = new Stats();
    LongAdder statsCancel = new LongAdder(); Flux<String> flux =
    Flux.just("foo", "bar")
    .doOnSubscribe(s -> stats.startTimer())
    .doFinally(type -> {
    stats.stopTimerAndRecordTiming();
    if (type == SignalType.CANCEL)
    statsCancel.increment();
    })
    .take(1);
  7. retry重新执行响应式流(原始流依然终止),retryBackoff提供指数退避算法。retryWhen需要一个Flux参数(可以用Retry.from(Function)工厂方法创建)。

    Flux.interval(Duration.ofMillis(250))
    .map(input -> {
    if (input < 3) return "tick " + input;
    throw new RuntimeException("boom");
    })
    .retry(1)
    .elapsed()
    .subscribe(System.out::println, System.err::println); Thread.sleep(2100);
  8. other:

    • 去空数据流: defaultIfEmpty返回默认值,或者switchIfEmpty返回不同的响应流。

    • 可以用timeout操作符,可以抛出TimeoutException,然后处理可以抛出TimeoutException异常

    • Reactor有一个Exceptions实用程序类,您可以使用它来确保仅在检查了异常的情况下包装异常:

    • Exceptions.propagate如有必要,使用该方法包装异常。它还throwIfFatal先调用 ,并且不包装RuntimeException。

    • 使用该Exceptions.unwrap方法来获取原始的未包装的异常(返回到特定于反应堆的异常的层次结构的根本原因):

    Flux<String> converted = Flux
    .range(1, 10)
    .map(i -> {
    try { return convert(i); }
    catch (IOException e) { throw Exceptions.propagate(e); }
    }); converted.subscribe(
    v -> System.out.println("RECEIVED: " + v),
    e -> {
    if (Exceptions.unwrap(e) instanceof IOException) {
    System.out.println("Something bad happened with I/O");
    } else {
    System.out.println("Something bad happened");
    }});

参考:Handling Errors

Project Reactor工厂方法和错误处理的更多相关文章

  1. JS学习十七天----工厂方法模式

    工厂方法模式 前言 今天自己看了一下自己写的部分博客,发现写的好丑....開始注意自己的排版!!可是偏亮也不是一朝一夕就完毕的,我尽量让它美丽一点.....每天美丽一点点 正文 工厂方法模式是一种实现 ...

  2. Project Reactor 响应式编程

    目录 一. 什么是响应式编程? 二. Project Reactor介绍 三. Reactor核心概念 Flux 1. just() 2. fromArray(),fromIterable()和 fr ...

  3. Spring学习记录(九)---通过工厂方法配置bean

    1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. package com.guigu.spring.factory; public class C ...

  4. 静态工厂方法VS构造器

    我之前已经介绍过关于构建者模式(Builder Pattern)的一些内容,它是一种很有用的模式用于实例化包含几个属性(可选的)的类,带来的好处是更容易读.写及维护客户端代码.今天,我将继续介绍对象创 ...

  5. java设计模式(二)---工厂方法模式

    2普通工厂方法模式 就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 2.1创建接口 /** * 发送接口 * Created by mrf on 2016/2/25. */ public ...

  6. [设计模式] javascript 之 工厂方法模式

    1. 简单工厂模式 说明:就是创建一个工厂类,里面实现了所对同一个接口的实现类的创建. 但是好像JavaScript 好像没有 接口 这号东西,所以我们去掉接口这个层; 当然,我们这里的 实现类 下的 ...

  7. C++设计模式——工厂方法模式

    本文版权归果冻说所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.» 本文链接:http://www.jellythink.com/arch ...

  8. Java设计模式---工厂方法模式(Factory-Method)

    一.普通工厂模式 建立一个工厂类,对实现了同一接口的一些类进行实例的创建 实例代码: 发送短信和邮件的例子,首先创建接口: public interface Sender { public void ...

  9. [改善Java代码]用枚举实现工厂方法模式更简洁

    工厂方法模式(Factory Method Patter)是"创建对象的接口",让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类.工厂方法模式在我们的开发工作中,经常会用到 ...

随机推荐

  1. 第40篇-JNIEnv和JavaVM

    下面介绍2个与JNI机制相关的类型JNIEnv和JavaVM. 1.JNIEnv JNIEnv一般是是由虚拟机传入,而且与线程相关的变量,也就说线程A不能使用线程B的JNIEnv.而作为一个结构体,它 ...

  2. PTA 银行排队问题之单队列多窗口服务 (25分)

    PTA 银行排队问题之单队列多窗口服务 (25分) 假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙.当有窗口空闲时,下一位顾客即去该窗口处理事务.当有多个窗口可选 ...

  3. vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(上)

    其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(上) 前面我们已经学习了一个成熟的脚手架(vue-cli),笔者希望通过这个脚手架快速搭建系统(或项目).而展开搭建最好 ...

  4. 如何系统学习C 语言(下)之 预处理命令篇

    大话c语言(下)之 预处理命令篇 预处理就是在编译之前,通过一些预处理命令对源代码进行管理和控制的过程. 由源代码得到可执行的程序,会经过预处理.编译.汇编和链接几个过程 预处理命令大致可以分为文件包 ...

  5. c++学习笔记(八)

    内联函数 概念 内联(inline)函数是c++为提高程序运行速度所做得一项改进. 与常规函数的区别不在于编写方式,而在于被调用时的运行机制不同----编译器使用函数代码替换函数调用. 引用内联函数时 ...

  6. SpringCloud远程服务调用

    笔记 在微服务中,若想要使用远程调用,需要引入spring-cloud-starter-openfeign(在使用注册中心的环境下) <dependency> <groupId> ...

  7. liunx下安装mysql(8.0.27)

    一.软件下载: 1.通过官网下载: https://dev.mysql.com/downloads/repo/yum/ 本文使用的系统为centos7,基于RedHat7的版本 2.下载完成后文件 m ...

  8. 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用

    Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...

  9. 【IDEA】颜色主题 Color Theme

    颜色主题 Color Theme 2020-09-08  08:35:44  by冲冲 1.本人的颜色主题:TasteTheRainbow.jar 链接:https://pan.baidu.com/s ...

  10. SpringServletContainerInitializer的代码流程

    SpringServletContainerInitializer 是spring中的一个class实现了servlet3.0规范的一个接口 implements ServletContainerIn ...