Flink Broadcast State 使用示例

本文介绍了Flink的Broadcast State特性,用于在任务间广播数据。通过一个改造后的wordcount示例,展示了如何创建、连接数据流和广播流,并在KeyedBroadcastProcessFunction中处理业务逻辑。同时,文章提醒在使用Broadcast State时需注意task间无法通信、元素顺序不确定性以及state快照等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是 Broadcast State

Broadcast State 是 Flink 1.5 引入的新特性。在开发过程中,如果遇到需要下发/广播配置、规则等低吞吐事件流到下游所有 task 时,就可以使用 Broadcast State 特性。下游的 task 接收这些配置、规则并保存为 BroadcastState, 将这些配置应用到另一个数据流的计算中 。英语好的同学可以直接移步 Flink 官方介绍
Broadcast State 区别于其他 operator state 的地方有:

  • Broadcast State 类似 Map 结构,可以 put get putAll remove 等
  • 必须有一条广播流和一条非广播流
  • 符合条件的 operator 可以有多个不同名字的 BroadcastState(疑惑:普通的 operator 也可以有多个不同名字的 state 吧,只是不是 BroadcastState。这么想也说得通了)

Broadcast state 示例

下面从一个示例来认识如何使用 Broadcast state. 我们对 wordcount 的例子都很熟悉,就简单改造下 wordcount吧。我们的改造目标是:实时控制输出结果中的单词长度
首先大体说一下思路,准备两个流,一个数据流(wordcount 需要统计的流) A,一个配置流(即广播流,后面有生成方法) B,这两个流的来源都可以自己定义,这里我们都用 kafka 作为输入源;然后用 A.keyBy(0).connect(B), 这里注意,一定是用数据流[.func()].connect(广播流),生成一个新的 BroadcastConnectedStream C;最后 C.process(new KeyedBroadcastProcessFunction<…>(…)) 进行逻辑处理。

1.数据流消费者
数据流就是 wordcount 程序统计的普通文本

FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>("input-topic-data",...);

2.广播流消费者
我们定义了广播流一条消息的格式为 {“length”:n} .其中 n 为数字,表示单词的最大长度。

FlinkKafkaConsumer<String> consumerBroadcast = new FlinkKafkaConsumer<>("input-topic-config",...);

3.生成数据流 A
这里对数据流进行了 wordcount 中的分词操作,输出流为 <单词, 数量, 生成时间>

DataStream<Tuple3<String, Integer, Long>> dataStream = env.addSource(consumer).flatMap(new LineSplitter());

4.生成广播流 B 并广播
我们知道,在 Flink 中,访问 state 前先要定义状态描述符(StateDescriptor). BroadcastState 的状态描述符是 MapStateDescriptor. MapStateDescriptor 的 value 类型即是广播流的元素类型,这个例子里是 Map<String,Object>。前面我们定义了广播的原始消息格式为 json 字符串,在这里我们通过 flatMap 函数转化成 Map<String,Object> 类型。转化完成后,调用 broadcast() 将这个流广播出去,这样,每个 task 都会收到广播流数据并在本地保存一份 BroadcastState,实际上,生成 BroadcastState 是在后续的 process() 中操作的。

// 定义 MapStateDescriptor
final MapStateDescriptor<String,Map<String,Object>> broadCastConfigDescriptor = new MapStateDescriptor<>("broadCastConfig",BasicTypeInfo.STRING_TYPE_INFO, new MapTypeInfo<>(String.class, Object.class));
// i.e. {"length":5}
BroadcastStream<Map<String,Object>> broadcastStream = env.addSource(consumerBroadcast).
         flatMap(new FlatMapFunction<String, Map<String,Object>>() {
   
                    // 解析 json 数据
                    private final ObjectMapper mapper = new ObjectMapper();
                    @Override
                    public void flatMap(String value, Collector<Map<String,Object>> out) {
   
                        try {
   
                             out.collect(mapper.readValue(value, Map.class));
                        } catch (IOException e) {
   
                             e.printStackTrace();
                             System.out.println(value);
                        }
                    }
               }
        // 这里需要调用 broadcast 广播出去,并且只能是 MapStateDescriptor 类型。可以指定多个
        ).broadcast(broadCastConfigDescriptor); //这里可以指定多个descriptor

5.连接两个流
接下来是两个流的连接部分了。前面说过,必须是 数据流.connect(广播流). 这里又分成两种情况

  • noKeyedStream.connect(BroadcastStream).process(new BroadcastProcessFunction<>(…)) 非 KeyedStream 连接 BroadcastStream 的,只能使用 BroadcastProcessFunction 函数处理连接逻辑
  • KeyedStream.connect(BroadcastStream).process(new KeyedBroadcastProcessFunction<>(…)) KeyedStream 连接 BroadcastStream 的,只能使用 KeyedBroadcastProcessFunction 函数处理连接逻辑
    KeyedBroadcastProcessFunction 比 BroadcastProcessFunction 多了计时器服务和获取当前 key 接口,当然,这两个功能不一定能用到。

我们这里使用的是 KeyedBroadcastProcessFunction<KS, IN1, IN2, OUT>:
KS 是 KeyedStream 中 key 的类型;IN1 是数据流(即非广播流)的元素类型;IN2 是广播流的元素类型;OUT 是两个流连接完成后,输出流的元素类型。

dataStream.keyBy(0)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值