本文主要研究下FluxInterval的机制

FluxInterval

reactor-core-3.1.3.RELEASE-sources.jar!/reactor/core/publisher/FluxInterval.java

/**
* Periodically emits an ever increasing long value either via a ScheduledExecutorService
* or a custom async callback function
* @see <a href="https://github.com/reactor/reactive-streams-commons">Reactive-Streams-Commons</a>
*/
final class FluxInterval extends Flux<Long> { final Scheduler timedScheduler; final long initialDelay; final long period; final TimeUnit unit; FluxInterval(
long initialDelay,
long period,
TimeUnit unit,
Scheduler timedScheduler) {
if (period < 0L) {
throw new IllegalArgumentException("period >= 0 required but it was " + period);
}
this.initialDelay = initialDelay;
this.period = period;
this.unit = Objects.requireNonNull(unit, "unit");
this.timedScheduler = Objects.requireNonNull(timedScheduler, "timedScheduler");
} @Override
public void subscribe(CoreSubscriber<? super Long> actual) {
Worker w = timedScheduler.createWorker(); IntervalRunnable r = new IntervalRunnable(actual, w); actual.onSubscribe(r); try {
w.schedulePeriodically(r, initialDelay, period, unit);
}
catch (RejectedExecutionException ree) {
if (!r.cancelled) {
actual.onError(Operators.onRejectedExecution(ree, r, null, null,
actual.currentContext()));
}
}
}
}

可以看到这里利用Scheduler来创建一个定时调度任务IntervalRunnable

IntervalRunnable

	static final class IntervalRunnable implements Runnable, Subscription,
InnerProducer<Long> {
final CoreSubscriber<? super Long> actual; final Worker worker; volatile long requested;
static final AtomicLongFieldUpdater<IntervalRunnable> REQUESTED =
AtomicLongFieldUpdater.newUpdater(IntervalRunnable.class, "requested"); long count; volatile boolean cancelled; IntervalRunnable(CoreSubscriber<? super Long> actual, Worker worker) {
this.actual = actual;
this.worker = worker;
} @Override
public CoreSubscriber<? super Long> actual() {
return actual;
} @Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.CANCELLED) return cancelled; return InnerProducer.super.scanUnsafe(key);
} @Override
public void run() {
if (!cancelled) {
if (requested != 0L) {
actual.onNext(count++);
if (requested != Long.MAX_VALUE) {
REQUESTED.decrementAndGet(this);
}
} else {
cancel(); actual.onError(Exceptions.failWithOverflow("Could not emit tick " + count + " due to lack of requests" +
" (interval doesn't support small downstream requests that replenish slower than the ticks)"));
}
}
} @Override
public void request(long n) {
if (Operators.validate(n)) {
Operators.addCap(REQUESTED, this, n);
}
} @Override
public void cancel() {
if (!cancelled) {
cancelled = true;
worker.dispose();
}
}
}

这里重点看requested变量,run方法每次判断requested,如果requested为0则销毁worker,否则则每次发射一个元素计数就减一
而subscriber如果有继续request的话,则会增加requested的值

实例1

    public static void main(String[] args) throws InterruptedException {
Flux<Long> flux = Flux.interval(Duration.ofMillis(1))
.doOnNext(e -> {
System.out.println(e);
}).doOnError(e -> e.printStackTrace()); System.out.println("begin to subscribe");
flux.subscribe(e -> {
System.out.println(e);
try {
TimeUnit.MINUTES.sleep(30);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
});
TimeUnit.MINUTES.sleep(30);
}

这个例子requested是Long.MAX_VALUE,但是由于subscribe的线程跟运行interval的线程一样,由于里头执行了sleep操作也导致interval的调度也跟着阻塞住了。

实例2

    public static void main(String[] args) throws InterruptedException {
Flux<Long> flux = Flux.interval(Duration.ofMillis(1))
.doOnNext(e -> {
System.out.println(e);
})
//NOTE 这里request prefetch=256个
.publishOn(Schedulers.newElastic("publish-thread"))
.doOnError(e -> e.printStackTrace()); System.out.println("begin to subscribe");
AtomicInteger count = new AtomicInteger(0);
//NOTE 得有subscribe才能触发request
flux.subscribe(e -> {
LOGGER.info("receive:{}",e);
try {
//NOTE 使用publishOn将subscribe与interval的线程分开
if(count.get() == 0){
TimeUnit.MINUTES.sleep(2);
}
count.incrementAndGet();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
});
TimeUnit.MINUTES.sleep(30);
}

使用publishOn将subscriber线程与interval线程隔离,使其sleep不阻塞interval
这里publishOn隐含了一个prefetch参数,默认是Queues.SMALL_BUFFER_SIZE即Math.max(16,Integer.parseInt(System.getProperty("reactor.bufferSize.small", "256")));

	public final Flux<T> publishOn(Scheduler scheduler) {
return publishOn(scheduler, Queues.SMALL_BUFFER_SIZE);
} final Flux<T> publishOn(Scheduler scheduler, boolean delayError, int prefetch, int lowTide) {
if (this instanceof Callable) {
if (this instanceof Fuseable.ScalarCallable) {
@SuppressWarnings("unchecked")
Fuseable.ScalarCallable<T> s = (Fuseable.ScalarCallable<T>) this;
try {
return onAssembly(new FluxSubscribeOnValue<>(s.call(), scheduler));
}
catch (Exception e) {
//leave FluxSubscribeOnCallable defer exception call
}
}
@SuppressWarnings("unchecked")
Callable<T> c = (Callable<T>)this;
return onAssembly(new FluxSubscribeOnCallable<>(c, scheduler));
} return onAssembly(new FluxPublishOn<>(this, scheduler, delayError, prefetch, lowTide, Queues.get(prefetch)));
}

这里使用Queues.get(prefetch)创建一个间接的队列来盛放元素

这个实例最后输出

//......
21:06:03.108 [publish-thread-2] INFO com.example.demo.FluxTest - receive:254
21:06:03.108 [publish-thread-2] INFO com.example.demo.FluxTest - receive:255
reactor.core.Exceptions$OverflowException: Could not emit tick 256 due to lack of requests (interval doesn't support small downstream requests that replenish slower than the ticks)
at reactor.core.Exceptions.failWithOverflow(Exceptions.java:215)
at reactor.core.publisher.FluxInterval$IntervalRunnable.run(FluxInterval.java:121)
at reactor.core.scheduler.PeriodicWorkerTask.call(PeriodicWorkerTask.java:59)
at reactor.core.scheduler.PeriodicWorkerTask.run(PeriodicWorkerTask.java:73)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

由于第一次request默认是256,之后在发射256个元素之后,subscriber没有跟上,导致interval的worker被cancel掉了,于是后续消费完256个元素之后,紧挨着就是OverflowException这个异常

小结

reactor本身并不依赖线程,只有interval,delayElements等方法才会创建线程。而reactor本身是观察者设计模式的扩展,采用push+backpressure模式,一开始调用subscribe方法就触发request N请求推送数据,之后publisher就onNext推送数据,直到complete或cancel。实例1是因为线程阻塞导致interval的onNext阻塞,实例2是interval被cancel掉导致flux关闭。

转载于:https://my.oschina.net/go4it/blog/1622063

FluxInterval实例及解析的更多相关文章

  1. 大数据应用日志采集之Scribe演示实例完全解析

    大数据应用日志采集之Scribe演示实例完全解析 引子: Scribe是Facebook开源的日志收集系统,在Facebook内部已经得到大量的应用.它能够从各种日志源上收集日志,存储到一个中央存储系 ...

  2. Java解析XML文档(简单实例)——dom解析xml

      一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object ...

  3. boot.img格式文件拆解实例结构解析

    以msm8226为例,讲解android源码编译生成boot.img的结构.boot.img包括boot.img header.kernel以及ramdisk文件系统.下面是对boot.img的结构进 ...

  4. 【SpringMVC架构】SpringMVC入门实例,解析工作原理(二)

    上篇博文,我们简单的介绍了什么是SpringMVC.这篇博文.我们搭建一个简单SpringMVC的环境,使用非注解形式实现一个HelloWorld实例,从简单入手,逐步深入. 环境准备 我们须要有主要 ...

  5. 邵国际: C 语言对象化设计实例 —— 命令解析器

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 邵国际 来源: 微信公众号linux阅码场(id: linuxdev) 内容简介 单片机工程师常常疑惑为什么 ...

  6. SwiftUI 官方画图实例详细解析

    前言 在前面几篇关于SwiftUI的文章中,我们用一个具体的基本项目Demo来学习了下SwiftUI,里面包含了常见的一些控件使用以及数据处理和地图等等,有兴趣的小伙伴可以去翻翻以前的文章,在前面总结 ...

  7. Argo 安装和 workflow 实例配置文件解析

    一.Argo 安装配置 1.1 Argo 安装 $ kubectl create ns argo $ kubectl apply -n argo -f https://raw.githubuserco ...

  8. 第一个SpringMVC实例和解析(HelloSpringMVC)

    1. 开发步骤: (1)增加Spring支持 下载Spring安装包和其依赖的commons-logging.jar,复制到项目Web应用的lib文件夹(WebRoot/WEB-INF/lib): S ...

  9. SQL Server的实例恢复解析

    同Oracle一样,SQL Server在非一致性关闭的时候也会进行实例恢复(Instance Recovery),本文根据stack overflow的文章介绍一些SQL Server实例恢复的知识 ...

随机推荐

  1. mac 升级到mavericks 安装php扩展现问题

    mac 升级到mavericks 安装php扩展现以下问题 grep: /usr/include/php/main/php.h: No such file or directory grep: /us ...

  2. 30 HashSet

    /* * 使用HashSet存储字符串并遍历 * * Set的特点: * 无序(存储和读取的顺序可能不一样) * 不允许重复 * 没有整数索引 于List正好相反 */ public class Ha ...

  3. matplotlib formatters

      Tick formatting is controlled by classes derived from Formatter. The formatteroperates on a single ...

  4. PHP 语法引用使用及实现

    说明 这里基于 php7.2.5 进行测试,php7 之后内部结构变化应该不是太大,但与 php5.X 有差别. 什么是引用 在 PHP 中引用是一种数据类型 (结构),是指 指向同一个类型的数据结构 ...

  5. 数据结构和算法(Golang实现)(15)常见数据结构-列表

    列表 一.列表 List 我们又经常听到列表 List数据结构,其实这只是更宏观的统称,表示存放数据的队列. 列表List:存放数据,数据按顺序排列,可以依次入队和出队,有序号关系,可以取出某序号的数 ...

  6. Linux下搭建接口自动化测试平台

    前言 我们今天来学习一下在Linux下如何搭建基于HttpRunner开发的接口自动化测试平台吧! 需要在Linux上提前准备的环境(下面是本人搭建时的环境): 1,Python 3.6.8 (可参考 ...

  7. MVC学习的心路历程

    2020/4/17 之前接触过三层架构,但是没有接触过mvc,所以有点蒙,所以现在在一步步构建思路. 1.了解MVC的发展,做一个简单的项目.

  8. stand up meeting 1/7/2016

    part 组员                今日工作              工作耗时/h 明日计划 工作耗时/h    UI 冯晓云 调研下滑条的存在问题,尝试替换方案     6 全面实行替换 ...

  9. E1. Send Boxes to Alice (Easy Version)

    题解: 保存每个1的位置.然后记录1的总个数cnt,如果存在一个k使得这个k是每个集合的倍数,那么为了使操作次数最小,这个k应该是cnt的质因子.(因为都是每个集合的数目1,使每个集合的数目变为2需要 ...

  10. 带权值的LCA

    例题:http://poj.org/problem?id=1986 POJ1986 Distance Queries Language: Default Distance Queries Time L ...