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实例恢复的知识 ...
随机推荐
- 配置一个mariadb数据库《二》
mariadb 配置一个数据库 案例4:配置一个数据库 4.1 问题 本例要求在虚拟机 ...
- 天天写order by,你知道Mysql底层执行原理吗?
前言 文章首发于微信公众号[码猿技术专栏]. 在实际的开发中一定会碰到根据某个字段进行排序后来显示结果的需求,但是你真的理解order by在 Mysql 底层是如何执行的吗? 假设你要查询城市是苏州 ...
- spring04
今天设计到的内容是是spring的sqel和ioc的生命周期和bean的后置处理器 具体的测试文件如下: <?xml version="1.0" encoding=" ...
- k8s集群搭建笔记(细节有解释哦)
本文中所有带引号的命令,请手动输入引号,不知道为什么博客里输入引号,总是自动转换成了中文 基本组成 pod:k8s 最小单位,类似docker的容器(也许) 资源清单:资源.资源清单语法.pod生命周 ...
- 37.3 net--TcpDemo1 大小写转换
需求:使用TCP协议发送数据,并将接收到的数据转换成大写返回 启动方式:先打开服务端,再打开客户端 客户端 package day35_net_网络编程.tcp传输; import java.io.I ...
- String 对象-->split() 方法
1.定义和用法 split() 方法用于把一个字符串分割成字符串数组. 语法: string.split(separator,limit) 参数: separator:可选.字符串或正则表达式,从该参 ...
- win10+ubuntu双系统修复ubuntu启动引导
因为windows是不能引导linux的,而每次win10升级或恢复都会将linux的启动引导覆盖掉,导致无法进入linux, 所以一直就禁止了win10更新.这几天win10出了点小毛病,所以就狠下 ...
- 怎么搭建python环境?很简单,就几步的事
现在学习python的人越来越多了,而学习python必备的就是搭建python环境,那么,到底怎么搭建python环境呢? 首先,你需要有安装包,这个去官网下载就可以了,如果不会的话,可以看文章底部 ...
- angularjs: draggable js
var startX = 0, startY = 0, x = 0, y = 0, minDragRang = 50; var targetContainer = angular.element(do ...
- Java中Date时间类
Date:表示特定的瞬间,精确到毫秒. 构造方法: Date():根据当前的默认毫秒值创建日期对象 Date(long date):根据给定的毫秒值创建日期对象 public static void ...
