工厂方法创建流

  • 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. JavaScript 事件循环

    JavaScript 事件循环 事件循环 任务队列 async/await 又是如何处理的呢 ? 定时器问题 阻塞还是非阻塞 实际应用案例 拆分 CPU 过载任务 进度指示 在事件之后做一些事情 事件 ...

  2. LeetCode-40. 组合总和 II C++(回溯法)

    回溯法本身是种暴力解法,虽然效率之类的比较低,但是写起来比较易懂和快.在提交之后的排名也挺低的,大概就超过8%左右.以后复习的时候再去看看题解,看看更高性能的算法.这里先暂时贴上回溯法的代码. 最后说 ...

  3. js实现一个小游戏(飞翔的jj)

    js实现一个小游戏(飞翔的jj) 源代码+素材图片在我的仓库 <!DOCTYPE html> <html lang="en"> <head> & ...

  4. 在k8s中收集jvm异常dump文件到OSS

    现状 加参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/test.dump 可以实现在jvm发生内存错误后 会生成dump文件 方便开 ...

  5. Effective C++ 总结笔记(四)

    五.实现 26.尽可能延后变量定义式的出现时间 尽可能延后变量定义式的出现,甚至应该尝试延后这份定义直到能够给他初值实参为止,这样不仅能避免构造和析构非必要对象,避免无意义的default行为,也可增 ...

  6. postman调试工具介绍及常用的快捷键收集

    关于Postman postman基础功能介绍 使用postman进行接口自动化测试 快捷键大全 简单操作 请求 工具栏 接口 窗口 数据编辑 关于Postman Postman是一款功能强大的网页调 ...

  7. CAP 5.2 版本发布通告

    前言 今天,我们很高兴宣布 CAP 发布 5.2 版本正式版,在这个版本中,我们主要致力于更好的优化使用体验以及支持新的 Transport,同时在该版本也进行了一些 bug 修复的工作. 自从 5. ...

  8. 菜鸡的Java笔记 - java 断言

    断言:assert (了解)        所谓的断言指的是在程序编写的过程之中,确定代码执行到某行之后数据一定是某个期待的内容        范例:观察断言 public class Abnorma ...

  9. myeclipse与tomcat,运行jsp程序

    一.myeclipse中配置JRE 步骤: 1.选择window->preferences->Java->Installed JREs 2.点击窗口右边的"add" ...

  10. 论文解读(node2vec)《node2vec Scalable Feature Learning for Networks》

    论文题目:<node2vec Scalable Feature Learning for Network>发表时间:  KDD 2016 论文作者:  Aditya Grover;Adit ...