Flink Idle 中的设计
哈哈… 今天是源码解析天,关于Idle 的,来吧,看看源码中是怎样检测数据源是否要标记为Idle。
//在StreamSourceContexts 类中可以看到, 内部类 WatermarkContext的构造方法中设置了
//IdleTimeOut,timeService(这个是一个定时执行的服务),以及调用了scheduleNextIdleDetectionTask()方法。
public WatermarkContext(
final ProcessingTimeService timeService,
final Object checkpointLock,
final long idleTimeout) {
this.timeService =
Preconditions.checkNotNull(timeService, "Time Service cannot be null.");
this.checkpointLock =
Preconditions.checkNotNull(checkpointLock, "Checkpoint Lock cannot be null.");
if (idleTimeout != -1) {
Preconditions.checkArgument(
idleTimeout >= 1, "The idle timeout cannot be smaller than 1 ms.");
}
this.idleTimeout = idleTimeout;
// 这个方法很重要,进去看一下。
scheduleNextIdleDetectionTask();
}
private void scheduleNextIdleDetectionTask() {
//如果IdleTimeout不 为-1,嘎,就向定时服务注册一个定时任务。
if (idleTimeout != -1) {
// reset flag; if it remains true when task fires, we have detected idleness
failOnNextCheck = true;
nextCheck =
this.timeService.registerTimer(
// 这里注册时间,当前时间+idleTimeout的时间
this.timeService.getCurrentProcessingTime() + idleTimeout,
// 这个一个定时任务,cool,再跳进去。
new IdlenessDetectionTask());
}
}
// 定时服务的Task会调用Callback的onProcessingTime(...)方法
private class IdlenessDetectionTask implements ProcessingTimeCallback {
@Override
public void onProcessingTime(long timestamp) throws Exception {
synchronized (checkpointLock) {
// set this to null now;
// the next idleness detection will be scheduled again
// depending on the below failOnNextCheck condition
nextCheck = null;
// 如果failOnNextCheck标识 默认为false,在调用scheduleNextIdleDetectionTask时,将该标识设置为true,
if (failOnNextCheck) {
// 调用Idle
markAsTemporarilyIdle();
} else {
//不然,就继续调用该方法。
//注意,这种看上去像是一个方法调用另外一个方法(容易栈溢出),但是不是哦,
//这里scheduleNextIdleDetectionTask()可是,向定时服务提交一个任务,即向另一个Thread 提交一个定时任务。所以安全。
scheduleNextIdleDetectionTask();
}
}
}
}
// 那failOnNextCheck 什么时候为false,下面贴的代码里有展示,是具体调用
//WatermarkContext类的相应的方法。有新数据collect 或者watermark 发射时会调用
WatermarkContext类的方法。
@Override
public void collect(T element) {
synchronized (checkpointLock) {
processAndEmitStreamStatus(StreamStatus.ACTIVE);
if (nextCheck != null) {
this.failOnNextCheck = false;
} else {
scheduleNextIdleDetectionTask();
}
processAndCollect(element);
}
}
@Override
public void collectWithTimestamp(T element, long timestamp) {
synchronized (checkpointLock) {
processAndEmitStreamStatus(StreamStatus.ACTIVE);
if (nextCheck != null) {
this.failOnNextCheck = false;
} else {
scheduleNextIdleDetectionTask();
}
processAndCollectWithTimestamp(element, timestamp);
}
}
@Override
public void emitWatermark(Watermark mark) {
if (allowWatermark(mark)) {
synchronized (checkpointLock) {
processAndEmitStreamStatus(StreamStatus.ACTIVE);
if (nextCheck != null) {
this.failOnNextCheck = false;
} else {
scheduleNextIdleDetectionTask();
}
processAndEmitWatermark(mark);
}
}
}
ok。除了上述,还有上篇文章提到的.withIdleness()这个就真的很简单了。
//调用 activity() 方法用户计数。
//这里时wm策略的方法 onEvent() 、onPeriodicEmit()
@Override
public void onEvent(T event, long eventTimestamp, WatermarkOutput output) {
watermarks.onEvent(event, eventTimestamp, output);
idlenessTimer.activity();
}
//如果这次计数和上次计数不同,就说明,有wm产生,如果上次计数和这次计数的相同,就获取一
//个起点时间,如果时钟的时间-起点时间>=IdleTimeOut.则Idle。
//下面的IdlenessTimer类,及方法将表现这一逻辑
@Override
public void onPeriodicEmit(WatermarkOutput output) {
if (idlenessTimer.checkIfIdle()) {
output.markIdle();
} else {
watermarks.onPeriodicEmit(output);
}
}
static final class IdlenessTimer {
/** The clock used to measure elapsed time. */
private final Clock clock;
/** Counter to detect change. No problem if it overflows. */
private long counter;
/** The value of the counter at the last activity check. */
private long lastCounter;
/**
* The first time (relative to {@link Clock#relativeTimeNanos()}) when the activity check
* found that no activity happened since the last check. Special value: 0 = no timer.
*/
private long startOfInactivityNanos;
/** The duration before the output is marked as idle. */
private final long maxIdleTimeNanos;
IdlenessTimer(Clock clock, Duration idleTimeout) {
this.clock = clock;
long idleNanos;
try {
idleNanos = idleTimeout.toNanos();
} catch (ArithmeticException ignored) {
// long integer overflow
idleNanos = Long.MAX_VALUE;
}
this.maxIdleTimeNanos = idleNanos;
}
public void activity() {
//开始计数
counter++;
}
public boolean checkIfIdle() {
// 和上次的计数不同,不用Idle,返回false。
if (counter != lastCounter) {
// activity since the last check. we reset the timer
lastCounter = counter;
startOfInactivityNanos = 0L;
return false;
} else // timer started but has not yet reached idle timeout
// 起点为0,将当前时钟的值赋予起点
if (startOfInactivityNanos == 0L) {
// first time that we see no activity since the last periodic probe
// begin the timer
startOfInactivityNanos = clock.relativeTimeNanos();
return false;
} else {
//起点不为0,且计数相同,就比较当前时钟和起点的差值是否大于 IdleTimeOut 时间。
return clock.relativeTimeNanos() - startOfInactivityNanos > maxIdleTimeNanos;
}
}
}
结束了。散花。。。欢迎留言