![](/img/trans.png)
[英]Project Reactor create multiple scheduler from the same Executor
[英]How to use Project Reactor's Scheduler with Executor based libraries?
Project Reactor 通过定义Scheduler
提供了一种很好的方法来定义代码运行的线程池。 它还为使用CompletableFuture
的库提供了一个桥梁,虽然Mono.fromFuture(..)
。
AWS 的DyanmoDB 异步客户端执行CompletableFuture
,它从java.util.concurrent.Executor
上的 API 调用返回。 默认情况下,它会创建一个由它也创建的线程池支持的Executor
。 结果是,即使是具有像Mono.fromFuture(..).subscribeOn(Schedulers.boundedElastic())
这样定义的Scheduler
流,也会在库创建的池中的线程上执行,而不是在Schedulers.boundedElastic()
的线程上执行。 所以我们看到线程名称像sdk-async-response-0-2
,而不是像boundedElastic-1
这样的名称。
幸运的是,该库允许我们提供我们自己的Executor
, 如下所示,所以我的问题是:
您如何构建一个
Executor
,该Executor
在运行时使用在该部分流上定义的Scheduler
的线程?
用例
我们有一个存储库类,它有一个findById
方法,我们需要调用者能够控制在哪个Scheduler
上运行,因为它在这些截然不同的上下文中使用:
Schedulers.boundedElastic()
调度Schedulers.boundedElastic()
上运行的 API 响应。尝试
我们已经尝试使用Schedulers.immediate()
和Runnable::run
定义一个Executor
,如下所示,但两者都导致在 Netty 事件循环线程上执行(示例名称: aws-java-sdk-NettyEventLoop-0-2
) ,不是来自定义的Scheduler
的线程。
DynamoDbAsyncClient.builder()
.asyncConfiguration(builder -> builder.advancedOption(
SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR,
runnable -> Schedulers.immediate().schedule(runnable)
))
.build();
DynamoDbAsyncClient.builder()
.asyncConfiguration(builder -> builder.advancedOption(
SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR,
Runnable::run
))
.build();
调查这个问题,我看到在特定线程上执行后需要观察元素。 准确地说,在这种情况下观察意味着*能够在某个特定线程上处理流中的值。 在 RxJava 中,我们有一个正确的操作符,就像这样,但在 Project Reactor 中,我们将相同的操作称为publishOn
。
因此, * 如果您想在Schedulers.boundedElastic()
上处理数据,那么您应该使用以下构造
Mono.fromFuture(..)
.publishOn(Schedulers.boundedElastic())
.subscribeOn
也有效???阅读之前的构造,您可能会开始担心,因为您 100% 确定
Mono.fromRunnable(..)
.subscribeOn(Schedulers.boundedElastic())
在线程boundedElastic-1
上发送onNext
,那么相同的fromFuture
有什么问题。
这里有一个技巧:
subscribeOn
与Futures
/ CompletableFuture
或任何可以在下面使用自己的异步机制的东西一起使用如果我们查看subscribeOn
背后发生的事情,您会发现如下内容:
// Simplified version of SubscribeOn operator
@Override
public void subscribe(CoreSubscriber<? super T> actual) {
Scheduler scheduler;
Publisher<T> parent;
scheduler.schedule(() -> parent.subscribe(actual));
}
这基本上意味着将在单独的线程上调用父级的subscribe
方法。
这种技术适用于fromRunnable
、 fromSupplier
、 fromCallable
因为它们的逻辑发生在subscribe
方法中:
@Override
public void subscribe(CoreSubscriber<? super T> actual) {
Operators.MonoSubscriber<T, T>
sds = new Operators.MonoSubscriber<>(actual);
actual.onSubscribe(sds);
// skiped some parts
T t = supplier.get();
if (t == null) {
sds.onComplete();
}
else {
sds.complete(t);
}
}
这意味着它几乎等于
scheduler.schedule(() -> {
T t = supplier.get();
if (t == null) {
sds.onComplete();
}
else {
sds.complete(t);
}
})
相比之下, fromFuture
工作要复杂得多。 一个简短的测验。
我们可以在哪个线程上观察到一个值? (假设在线程 Main 上执行,并且任务在 ForkJoinPool 上执行)
var future = CompletableFuture
.supplyAsync(() -> {
return value;
})
... // some code here, does not metter just code
future.thenAccept(value -> {
System.out.println(Thread.currentThread())
});
和正确答案....🥁🥁🥁🥁🥁🥁
它可能是线程主
或者它可能是来自 ForkJoinPool 的线程
...
因为它很活泼......而且在这一点上,我们消费了价值,价值可能已经交付,所以我们只是在读取器线程(线程 Main)上读取volatile
字段,否则,线程 Main 将设置一个acceptor
因此稍后将在ForkJoinPool
线程上调用接受器。
是的,这就是为什么当您将fromFuture
与subscribeOn
一起使用时,不能保证subscribeOn
线程会观察给定CompletableFuture
的值。
这就是为什么publishOn
是确保值处理发生在所需线程上的唯一方法。
publishOn
???是和否。 这取决于。
如果您使用Mono
- 在 99% 的情况下,如果您想确保您的数据处理发生在特定线程上,您可以使用publishOn
- 始终使用publishOn
。
不要担心潜在的开销,即使您不小心使用了 Project Reactor,它也会照顾您。 Project Reactor 有几个优化,可以在运行时用subscribeOn
替换你的publishOn
(如果它是安全的而不破坏行为),那么你会得到最好的。
Scheduelr
的兔子洞Schedulers.immediate()
它几乎是无操作调度程序,基本上可以
Schedulers.immediate().scheduler(runnable) {
runnable.run()
}
是的,它对反应堆用户没有任何用处,我们仅将其用于内部需求。
有两种选择:
1.a) 创建您的有界Executor
。 (例如Executors.fixed...
)
1.b) 如果您想获得周期性任务和延迟任务的力量,请创建您的有界ScheduledExecutorService
2) 使用Schedulers.fromExecutorXXX
API 从您的执行Schedulers.fromExecutorXXX
创建Scheduler
Schedulers.fromExecutorXXX
3)在命令式世界中使用你的有界Executor
,使用你的Scheduler
,它是反应世界的有界Executor
的包装器
即将推出...
即将推出
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.