FluxInterval实例及解析
序
本文主要研究下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实例及解析的更多相关文章
- 大数据应用日志采集之Scribe演示实例完全解析
大数据应用日志采集之Scribe演示实例完全解析 引子: Scribe是Facebook开源的日志收集系统,在Facebook内部已经得到大量的应用.它能够从各种日志源上收集日志,存储到一个中央存储系 ...
- Java解析XML文档(简单实例)——dom解析xml
一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object ...
- boot.img格式文件拆解实例结构解析
以msm8226为例,讲解android源码编译生成boot.img的结构.boot.img包括boot.img header.kernel以及ramdisk文件系统.下面是对boot.img的结构进 ...
- 【SpringMVC架构】SpringMVC入门实例,解析工作原理(二)
上篇博文,我们简单的介绍了什么是SpringMVC.这篇博文.我们搭建一个简单SpringMVC的环境,使用非注解形式实现一个HelloWorld实例,从简单入手,逐步深入. 环境准备 我们须要有主要 ...
- 邵国际: C 语言对象化设计实例 —— 命令解析器
本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 邵国际 来源: 微信公众号linux阅码场(id: linuxdev) 内容简介 单片机工程师常常疑惑为什么 ...
- SwiftUI 官方画图实例详细解析
前言 在前面几篇关于SwiftUI的文章中,我们用一个具体的基本项目Demo来学习了下SwiftUI,里面包含了常见的一些控件使用以及数据处理和地图等等,有兴趣的小伙伴可以去翻翻以前的文章,在前面总结 ...
- Argo 安装和 workflow 实例配置文件解析
一.Argo 安装配置 1.1 Argo 安装 $ kubectl create ns argo $ kubectl apply -n argo -f https://raw.githubuserco ...
- 第一个SpringMVC实例和解析(HelloSpringMVC)
1. 开发步骤: (1)增加Spring支持 下载Spring安装包和其依赖的commons-logging.jar,复制到项目Web应用的lib文件夹(WebRoot/WEB-INF/lib): S ...
- SQL Server的实例恢复解析
同Oracle一样,SQL Server在非一致性关闭的时候也会进行实例恢复(Instance Recovery),本文根据stack overflow的文章介绍一些SQL Server实例恢复的知识 ...
随机推荐
- hadoop+zookeeper集群高可用搭建
hadoop+zookeeper集群高可用搭建 Senerity 发布于 2 ...
- 数据库学习 day2 检索数据
上一节我们介绍了什么是数据库,以及一些基本的数据库术语 这一课介绍使用SELECT语句从表中检索一个或多个数据列. 关键字(Keyword) 作为SQL组成部分的保留字.关键字不能用作表和列的名字(类 ...
- 【PHP】流程控制
一. 顺序结构 a) PHP的脚本从上到下,从左往右的执行顺序就是顺序结构! 二. 分支结构 a) 单一的分支结构 If ( 条件表达式 ) { 程序体: } 执行过程 ...
- Python常见数据结构-Tuple元组
Python Tuple基本特点 元组与列表类似,不同之处在于元组的元素不能修改. 与字符串和列表一样,可以根据下标进行切片索引. 元组使用小括号,单一元素的元组定义是必须加一个逗号. Python ...
- Python 0(安装及初步使用+学习资源推荐)
不足之处,还请见谅,请指出不足.本人发布过的文章,会不断更改,力求减少错误信息. Python安装请借鉴网址https://www.runoob.com/python/python-install.h ...
- 初探CI,Github调戏Action手记——自动构建并发布
前言 最近在做脚本的说明文档时使用了vuepress这个东西 前端实在是菜,只能随便写写了 正常写完md文件之后推送至github做版本控制 而前端页面的生成则是在本地,部署也是在本地手工进行 一套下 ...
- Dubbo学习系列之十八(Skywalking服务跟踪)
我们知道,微服务不是独立的存在,否则就不需要微服务这个架构了,那么当发起一次请求,如何知道这次请求的轨迹,或者说遇到响应缓慢. 请求出错的情况,我们该如何定位呢?这就涉及到APM(Applicatio ...
- bat中的特殊字符,以及需要在bat中当做字符如何处理
bat中的特殊字符,以及需要在bat中当做字符如何处理 (2014-02-27 21:16:55) 转载▼ 标签: bat 特殊字符 分类: develop bat中的特殊字符,以及需要在bat中当做 ...
- 多级菜单初写(dict使用)
#!/usr/bin/env python3# -*- coding:utf-8 -*-# name:zzyumap = { "中国":{ "北京":{ &qu ...
- Nginx+uWSGI+Python+Django构建必应高清壁纸站
写在前面 做这个网站的初衷是因为,每次打开必应搜索搜东西的时候都会被上面的背景图片吸引,我想必应的壁纸应该是经过专业人员精选出来的,我甚至会翻看以前的历史图片,唯一美中不足的是必应的首页只能查看最多7 ...
