1、No ExecutorFactory found to execute the application
java.lang.IllegalStateException: No ExecutorFactory found to execute the application.
at org.apache.flink.core.execution.DefaultExecutorServiceLoader.getExecutorFactory(DefaultExecutorServiceLoader.java:84)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.executeAsync(StreamExecutionEnvironment.java:1931)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1836)
at org.apache.flink.streaming.api.environment.LocalStreamEnvironment.execute(LocalStreamEnvironment.java:70)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1822)
at org.eb.alert.app.AlertOperator.main(AlertOperator.java:101)
官网Release Notes - Flink 1.11
所以需要增加flink-clients的依赖。
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
2、时间语义
从flink 1.12.0开始默认的时间语义为event-time。所以不需要再调用:
StreamExecutionEnvironment.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
来指定时间语义了。
3、批流一体
在 Flink 1.12.0 中,默认执行模式为 STREAMING,要将作业配置为以 BATCH 模式运行,可以在提交作业的时候,设置参数 execution.runtime-mode:
$ bin/flink run -Dexecution.runtime-mode=BATCH examples/batch/WordCount.jar
或者在代码中指定:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setRuntimeMode(RuntimeMode.BATCH);
官网建议使用参数设置的方法,提高代码灵活性。
4、assignTimestampsAndWatermarks(AssignerWithPeriodicWatermarks<T> timestampAndWatermarkAssigner) 被标记为deprecated了。
因为该方法中用到的watermark生成器接口也被标记为deprecated了。推荐使用新的接口:
assignTimestampsAndWatermarks(WatermarkStrategy)
新的接口支持watermark空闲,并且不再区别periodic和punctuated。
例如:
/**
* 当没有新数据到达时,WatermarkGenerator是无法生成watermark的,这会导致任务无法继续运行。
* 例如kafka topic有2个partition,
* 其中一个partition数据量比较少,可能存在长时间没有新数据的情况,这就导致该watermark一直是
* 较早数据产生的。即使另一个partition始终有新数据,那么任务也不会往下运行,
* 因为水印对齐取的是最小值。为了解决这个问题,WatermarkStrategy提供了withIdleness方法,
* 允许传入一个timeout的时间。
*/
WatermarkStrategy
.<Tuple2<Long, String>>forBoundedOutOfOrderness(Duration.ofSeconds(20))
.withIdleness(Duration.ofMinutes(1));
WatermarkStrategy接口定义了如何在流数据中生成watermark。WatermarkStrategy是
WatermarkGenerator和TimestampAssigner的构造器,前者用于生成watermark,
后者用于指定一条记录的内部时间戳。
WatermarkStrategy接口包含三个部分:
1)需要实现的方法
/**
* 实例化一个根据此策略生成watermark的WatermarkGenerator。
* Instantiates a WatermarkGenerator that generates watermarks according to this strategy.
*/
@Override
WatermarkGenerator<T> createWatermarkGenerator(WatermarkGeneratorSupplier.Context context);
关于该方法的实现可以查看源码,例如:
WatermarkStrategyWithTimestampAssigner,这是一个final class,实现了WatermarkStrategy接口,并且
重写了TimestampAssigner。
2)WatermarkStrategy基本构造方法
3)WatermarkStrategy普通的内置构造方法以及基于WatermarkGeneratorSupplier的构造方法
/**
* 为乱序的情况创建watermark strategy,设置一个最大延迟时间。
* Creates a watermark strategy for situations where records are out of order, but you can place
* an upper bound on how far the events are out of order. An out-of-order bound B means that
* once the an event with timestamp T was encountered, no events older than {@code T - B} will
* follow any more.
*
* <p>The watermarks are generated periodically. The delay introduced by this watermark
* strategy is the periodic interval length, plus the out of orderness bound.
*
* @see BoundedOutOfOrdernessWatermarks
*/
static <T> WatermarkStrategy<T> forBoundedOutOfOrderness(Duration maxOutOfOrderness) {
return (ctx) -> new BoundedOutOfOrdernessWatermarks<>(maxOutOfOrderness);
}
5、关于WatermarkGenerator
5.1 Periodic WatermarkGenerator
这种方式就是定期生成watermark。时间间隔是通过
ExecutionConfig.setAutoWatermarkInterval(Long l)
定义的。周期性的调用onPeriodicEmit()方法,如果新的watermark不为null,并且大于之前的watermark,则发出。
env.getConfig().setAutoWatermarkInterval(0)
表示禁用watermark。
5.2 内置的watermark Generators
如果不存在乱序的情况,即event的时间戳是单调递增的,则
WatermarkStrategy.forMonotonousTimestamps();
如果存在乱序,则设置延迟时间。
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(10));
例如:
dsOperator.assignTimestampsAndWatermarks(
WatermarkStrategy.<Map<String,Object>>forBoundedOutOfOrderness(
Duration.ofSeconds(jobConfig.getFlinkJobConfigs().getWindowMaxOutOfOrderness()))
.withTimestampAssigner(new MyTimeAssigner(task.getTimestamp())))