四、Flink使用广播状态和定时器实现word_join_count有效时间1分钟

1、版本说明
在这里插入图片描述
2、代码实现

import org.apache.flink.api.common.state.BroadcastState;
import org.apache.flink.api.common.state.MapState;
import org.apache.flink.api.common.state.MapStateDescriptor;
import org.apache.flink.api.common.state.ReadOnlyBroadcastState;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.BroadcastStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction;
import org.apache.flink.util.Collector;

public class BroadcastAndTimer {
    public static void main(String[] args) throws Exception {
        // 创建flink执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 模拟两个输入流
        // id,count
        DataStreamSource<String> main = env.socketTextStream("localhost", 8888);
        // id,name
        DataStreamSource<String> line = env.socketTextStream("localhost", 9999);

        // 广播状态是一种特殊的operatorState,创建状态描述器
        MapStateDescriptor<String, String> mapStateDescriptor = new MapStateDescriptor<>("rule", String.class, String.class);

        // 创建广播状态
        BroadcastStream<String> broadcastStream = line.broadcast(mapStateDescriptor);

        //非广播流和广播流进行关联
        main.keyBy(e -> e.split(",")[0])
                .connect(broadcastStream)
                .process(new KeyedBroadcastProcessFunction<String, String, String, Tuple2<String, Integer>>() {
                    private MapState<String, Integer> countState;

                    // 定时器触发时执行
                    @Override
                    public void onTimer(long timestamp,
                                        KeyedBroadcastProcessFunction<String, String, String, Tuple2<String, Integer>>.OnTimerContext ctx,
                                        Collector<Tuple2<String, Integer>> out) throws Exception {

                        System.out.println("定时器被触发了,清除状态中的数据");
                        countState.clear();
                    }

                    // 处理非广播流
                    @Override
                    public void processElement(String value,
                                               KeyedBroadcastProcessFunction<String, String, String, Tuple2<String, Integer>>.ReadOnlyContext ctx,
                                               Collector<Tuple2<String, Integer>> out) throws Exception {
                        // 获取广播状态
                        ReadOnlyBroadcastState<String, String> state = ctx.getBroadcastState(mapStateDescriptor);

                        // 用于记录count的结果
                        countState = getRuntimeContext().getMapState(new MapStateDescriptor<String, Integer>("count", String.class, Integer.class));

                        // 将广播流和非广播流进行匹配,处理非广播流中数据 id/count
                        String[] fields = value.split(",");
                        String id = fields[0];
                        Integer count = Integer.valueOf(fields[1]);

                        // 获取广播流中的数据 id/name
                        String name = state.get(id);

                        // 注册处理时间定时器,设置触发时间为 1分钟
                        ctx.timerService().registerProcessingTimeTimer(System.currentTimeMillis() + 1000 * 60);
                        System.out.println(name + "=>有效时间1分钟");

                        // 读取历史数据中的 count
                        Integer oldCount = countState.get(name);
                        if (oldCount == null) {
                            oldCount = 0;
                        }
                        count += oldCount;

                        // 更新状态中的数据
                        countState.put(name, count);

                        out.collect(new Tuple2<String, Integer>(name, count));
                    }

                    // 处理广播流
                    @Override
                    public void processBroadcastElement(String value,
                                                        KeyedBroadcastProcessFunction<String, String, String, Tuple2<String, Integer>>.Context ctx,
                                                        Collector<Tuple2<String, Integer>> out) throws Exception {
                        // 获取广播状态
                        BroadcastState<String, String> broadcastState = ctx.getBroadcastState(mapStateDescriptor);

                        // 对状态进行处理
                        String[] fields = value.split(",");

                        // 将数据添加到广播状态中 id/name
                        broadcastState.put(fields[0], fields[1]);

                    }
                }).print();

        env.execute();
    }
}
3、执行结果

1)开启 8888 和 9999 端口,模拟两个输入流

1.开启两个端口

nc -lk 8888
nc -lk 9999

2.先输入9999端口数据

1,a
2,c
3,d

3.再输入8888端口数据

1,10
2,20
3,30

此时控制台输出结果为:

a=>有效时间1分钟
4> (a,10)
c=>有效时间1分钟
2> (c,20)
d=>有效时间1分钟
3> (d,30)

4.在定时器触发前,输入8888端口数据

1,100
2,100
3,100

此时控制台输出结果为:

a=>有效时间1分钟
4> (a,110)
c=>有效时间1分钟
2> (c,120)
d=>有效时间1分钟
3> (d,130)

5.待定时器触发后,输入8888端口数据

1,1000
2,2000
3,3000

此时控制台输出结果为:

定时器被触发了,清除状态中的数据
定时器被触发了,清除状态中的数据
定时器被触发了,清除状态中的数据

a=>有效时间1分钟
4> (a,1000)
c=>有效时间1分钟
2> (c,2000)
d=>有效时间1分钟
3> (d,3000)

你可能感兴趣的:(flink)