在看flink Window 機制的相關博客時,看到有個同學的需求很有趣,
"如何讓一個DataStream中的某個字段與21天前的該字段做比較?"
該同學給定了一個大小21天,每一天滑動一次的window
解決方案:
在其TimeWindow上進行修改,挖空中間不需要的20天,整出來一個門字形的TimeWindow,
這樣只剩下第一天和最後一天的兩個門腳數據,以進行後續的操作,如比較
具體實現依靠自定義Evictor實現,
這樣只需要應用該GantryTimeEvictor 即可
keyedStream
.window(SlidingEventTimeWindows.of(Time.days(21), Time.days(
.evictor(GantryTimeEvictor.of(Time.days(1)));
代碼如下:
package com.run;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.streaming.api.windowing.evictors.Evictor;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.streaming.runtime.operators.windowing.TimestampedValue;
import java.util.Iterator;
/**
* @Author MrBlack
* @Date 2019/12/17 017 下午 6:43
**/
public class GantryTimeEvictor implements Evictor<Object, TimeWindow> {
// 雙腳寬度
private final long gantryFootSize;
// 是否在應用窗口後驅除
private final boolean doEvictAfter;
public GantryTimeEvictor(long gantryFootSize) {
this.gantryFootSize = gantryFootSize;
this.doEvictAfter = false;
}
public GantryTimeEvictor(long gantryFootSize, boolean doEvictAfter) {
this.gantryFootSize = gantryFootSize;
this.doEvictAfter = doEvictAfter;
}
/**
* 在應用窗口前驅除不需要的元素
*
* @param elements 當前在窗口中的元素
* @param size 窗口中元素數量
* @param timeWindow 當前窗口
* @param ctx Evictor上下文
*/
@Override
public void evictBefore(Iterable<TimestampedValue<Object>> elements, int size, TimeWindow timeWindow, EvictorContext ctx) {
if (!this.doEvictAfter) {
this.evict(elements, size, timeWindow, ctx);
}
}
/**
* 在應用窗口後驅除不需要的元素
*/
@Override
public void evictAfter(Iterable<TimestampedValue<Object>> elements, int size, TimeWindow timeWindow, EvictorContext ctx) {
if (this.doEvictAfter) {
this.evict(elements, size, timeWindow, ctx);
}
}
private void evict(Iterable<TimestampedValue<Object>> elements, int size, TimeWindow timeWindow, EvictorContext ctx) {
if (this.hasTimestamp(elements)) {
long evictCutBegin = timeWindow.getStart() + this.gantryFootSize;
long evictCutEnd = timeWindow.getEnd() - this.gantryFootSize;
Iterator iterator = elements.iterator();
while (iterator.hasNext()) {
TimestampedValue<Object> record = (TimestampedValue) iterator.next();
if (record.getTimestamp() >= evictCutBegin && record.getTimestamp() <= evictCutEnd) {
iterator.remove();
}
}
}
}
/**
* 判斷元素是否有時間戳
*
* @param elements
* @return
*/
private boolean hasTimestamp(Iterable<TimestampedValue<Object>> elements) {
Iterator<TimestampedValue<Object>> it = elements.iterator();
return it.hasNext() && it.next().hasTimestamp();
}
public static GantryTimeEvictor of(Time gantryFootSize) {
return new GantryTimeEvictor(gantryFootSize.toMilliseconds());
}
public static GantryTimeEvictor of(Time gantryFootSize, boolean doEvictAfter) {
return new GantryTimeEvictor(gantryFootSize.toMilliseconds(), doEvictAfter);
}
}