Flink定时器

6.7 定时器

6.7.1 定时器API

以keyBy()之后的键控流的定时器为例:

//TODO 定时器 API
//1. 注册定时器
ctx.timerService().registerProcessingTimeTimer();
ctx.timerService().registerEventTimeTimer();

//2. 删除定时器
ctx.timerService().deleteProcessingTimeTimer();
ctx.timerService().deleteEventTimeTimer();

//3. 获取当前时间的进展
ctx.timerService().currentProcessingTime();
ctx.timerService().currentWatermark()
6.7.2 定时器的使用

基于处理时间的定时器 & 基于事件时间的定时器

public class Flink23_Process_Timer {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        SingleOutputStreamOperator<WaterSensor> sensorDS = env
                .socketTextStream("hadoop102", 9999)
                .map(new MapFunction<String, WaterSensor>() {
                    @Override
                    public WaterSensor map(String value) throws Exception {
                        String[] dats = value.split(",");
                        return new WaterSensor(
                                dats[0],
                                Long.valueOf(dats[1]),
                                Integer.valueOf(dats[2])
                        );
                    }
                })
                .assignTimestampsAndWatermarks(
                        WatermarkStrategy
                                .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3))
                                .withTimestampAssigner(new SerializableTimestampAssigner<WaterSensor>() {
                                    @Override
                                    public long extractTimestamp(WaterSensor element, long recordTimestamp) {
                                        return element.getTs() * 1000L;
                                    }
                                })
                );

        sensorDS
                .keyBy(sensor -> sensor.getId())
                .process(new KeyedProcessFunction<String, WaterSensor, String>() {
                    @Override
                    public void processElement(WaterSensor value, Context ctx, Collector<String> out) throws Exception {
                        //获取的是事件时间
//                        Long timestamp = ctx.timestamp();
                        //TODO 定时器 API
                        //1. 注册定时器
//                        ctx.timerService().registerProcessingTimeTimer();
//                        ctx.timerService().registerEventTimeTimer();

                        //2. 删除定时器
//                        ctx.timerService().deleteProcessingTimeTimer();
//                        ctx.timerService().deleteEventTimeTimer();

                        //3. 获取当前时间的进展
//                        ctx.timerService().currentProcessingTime();
//                        ctx.timerService().currentWatermark()

                        //TODO 定时器使用
                        //TODO 1 处理时间
//                        long processTs = ctx.timerService().currentProcessingTime() + 5000L;
//                        System.out.println("注册定时器,ts=" + processTs);
//                        ctx.timerService().registerProcessingTimeTimer(processTs);
                        //TODO 2 事件时间
                        long eventTs = 5000L;
                        System.out.println("注册定时器,ts=" + eventTs);
                        ctx.timerService().registerEventTimeTimer(eventTs);

                    }

                    /*
                     * @Description //TODO 定时器触发:表示时间进展到了 注册的那个时间
                     * @Param [timestamp, ctx, out] 触发的时间 也就是注册的那个时间, 上下文, 采集器
                     * @return void
                     **/
                    @Override
                    public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
                        System.out.println("定时器触发了,timestamp = " + timestamp);
                    }
                })
                .print();
        
        env.execute();
    }
}
6.7.3 定时器源码分析
1、定时器注册?
    => 每调用一次 register方法,都会 new一个定时器对象,并把定时器对象,放入一个 队列 里
    => 这个 队列 add 对象,会去重 => 如果重复注册同一个时间的定时器,只会放入一个对象
    => 去重,范围是同一分组,组内去重

2、定时器触发?
    watermark >= 注册的时间
    =>多个分组的多个定时器,都会触发。
    => 为什么sensor_2,9,1,能触发sensor_1分组的数据?
    	因为watermark表示是一个时间进展,它不会分组的,达到之后多个分组的定时器都会触发。

"思考:为什么定时器延迟了 1s触发?"
    因为 watermark = 最大事件时间 - 乱序程度 - 1ms, 减去了 1ms
    例如,乱序程度设置3s,注册了一个 10s的定时器,
    当 eventtime = 13s时, watermark = 13s - 3s -1ms = 9999ms,小于 注册的时间 10s,无法触发。
    只有当 eventtime = 14s, watermark = 14s -3 - 1ms = 10999ms, 大于 注册的时间 10s,可以触发。



eventTimeTimersQueue.add(new TimerHeapInternalTimer<>(time, (K) keyContext.getCurrentKey(), namespace));
    => 每调用一次 register方法,都会 new一个定时器对象
    => 把定时器对象,放入一个 队列 里
    => 这个 队列 add对象,会去重 => 如果重复注册同一个时间的定时器,只会放入一个对象

timer.getTimestamp() <= time
    => timer.getTimestamp() 就是定时器注册的时间
    => time = watermark.getTimestamp(),就是 watermark
    => 注册的时间 <= watermark
6.7.4 定时器练习题

监控水位传感器的水位值,如果水位值在五分钟之内(processing time)连续上升,则报警。

public class Flink24_Process_TimerPractice {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        env
                .socketTextStream("hadoop102", 9999)
                .map(new MapFunction<String, WaterSensor>() {
                    @Override
                    public WaterSensor map(String value) throws Exception {
                        String[] dats = value.split(",");
                        return new WaterSensor(
                                dats[0],
                                Long.valueOf(dats[1]),
                                Integer.valueOf(dats[2])
                        );
                    }
                })
                .assignTimestampsAndWatermarks(WatermarkStrategy
                        .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3))
                        .withTimestampAssigner(new SerializableTimestampAssigner<WaterSensor>() {
                            @Override
                            public long extractTimestamp(WaterSensor element, long recordTimestamp) {
                                return element.getTs() * 1000L;
                            }
                        })
                )
                .keyBy(sensor -> sensor.getId())
                .process(new KeyedProcessFunction<String, WaterSensor, String>() {
                    private long timeTs = 0L;
                    private int lastVC = 0;
                    @Override
                    public void processElement(WaterSensor value, Context ctx, Collector<String> out) throws Exception {
                        if (timeTs == 0){
                            //第一条数据来的时候注册一个定时器
                            ctx.timerService().registerEventTimeTimer(ctx.timestamp() + 5000L);
                            timeTs = ctx.timestamp() + 5000L;
                        }
                        //之后来的数据,都与上一条数据比较,判断水位值上升还是下降
                        if (value.getVc() < lastVC){
                            //只要遇到下降,就删除掉定时器
                            ctx.timerService().deleteEventTimeTimer(timeTs);
                            //注册新的定时器
                            ctx.timerService().registerEventTimeTimer(ctx.timestamp() + 5000L);
                            timeTs = ctx.timestamp() + 5000L;
                        }
                        //更新水位值
                        lastVC = value.getVc();
                    }

                    @Override
                    public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
                        //触发后,重置保存的ts,避免影响后面的检测
                        timeTs = 0L;
                        System.out.println("传感器=" + ctx.getCurrentKey() + "在ts=" + timestamp + "监测到水位连续5s上升");
                    }
                })
                .print("result");
        
        env.execute();
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Flink提供了两种类型的定时器:事件时间定时器和处理时间定时器。 事件时间定时器基于事件时间,可以在数据流中插入一个事件时间戳,一旦到达指定时间,就会触发定时器。事件时间定时器适用于需要等待一段时间以获取完整结果的场景,例如处理窗口。 处理时间定时器基于处理时间,可以在数据到达后的固定时间间隔后触发定时器。处理时间定时器适用于需要在一定时间间隔内执行某个操作的场景,例如清理过期数据。 下面是一个使用处理时间定时器的示例代码: ``` DataStreamSource<String> source = env.fromElements("a", "b", "c"); source .map(new MapFunction<String, Tuple2<String, Long>>() { @Override public Tuple2<String, Long> map(String value) throws Exception { return Tuple2.of(value, System.currentTimeMillis() + 10000L); } }) .keyBy(0) .process(new KeyedProcessFunction<String, Tuple2<String, Long>, String>() { @Override public void processElement(Tuple2<String, Long> value, Context ctx, Collector<String> out) throws Exception { // 注册处理时间定时器,10秒后触发 ctx.timerService().registerProcessingTimeTimer(value.f1); // 保存状态 ValueState<String> state = getRuntimeContext().getState(new ValueStateDescriptor<>("value-state", String.class)); state.update(value.f0); } @Override public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception { // 处理时间定时器触发,输出状态中的值 ValueState<String> state = getRuntimeContext().getState(new ValueStateDescriptor<>("value-state", String.class)); out.collect(state.value()); } }) .print(); env.execute(); ``` 在上面的示例中,我们先从一个字符串数据源中获取数据,并使用MapFunction将每个字符串与当前时间戳组成一个Tuple2。然后,我们使用KeyedProcessFunction对每个Tuple2进行处理,其中我们注册了处理时间定时器,并将状态保存在ValueState中。当定时器触发时,我们可以从状态中获取值并输出。 需要注意的是,处理时间定时器的触发时间是相对于 Flink JobManager 的机器时间的,而不是相对于数据流中的事件时间。因此,在使用处理时间定时器时应特别注意处理时间与事件时间之间的差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最佳第六六六人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值