Flink DataStream 广播状态模式

本文深入探讨FlinkDataStream中的广播状态模式,介绍其在处理流数据时如何将部分数据广播到所有下游任务,实现与另一流数据的有效结合。文章详细讲解了广播状态的特性,包括它是MapState类型,仅适用于包含广播流和非广播流的特定Operator。同时,通过KeyedStream和Non-KeyedStream连接广播流的实例,展示了如何在Flink中具体应用广播状态。

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

Flink DataStream 广播状态模式

我们使用State描述了Operator State,在恢复时,可以修改并行度重新分配Operator State(偶分裂再分配方式),或者使用Union的方式(联合重新分发)恢复并行任务。

Operator State还有一种广播状态模式(Broadcast State)

引入广播状态是为了支持这样的用例,其中来自一个流的一些数据需要被广播到所有下游任务,其中它被本地存储并用于处理另一个流上的所有传入元素。作为广播状态可以作为自然拟合出现的示例,可以想象包含一组规则的低吞吐量流,我们希望针对来自另一个流的所有元素进行评估

考虑到上述类型的用例,广播状态与其他运营商状态的不同之处在于:

  • 他是一个MapState

  • 它仅适用于具有特定的Operator作为输入一个广播流和一个非广播流

  • 这样的运营商可以具有不同名称的多个广播状态

Keyed StreamNon-Keyed Stream与一个BroadcastStream连接,非广播流可以通过调用connect()来完成,并将其BroadcastStream作为参数。这将返回一个BroadcastConnectedStream,我们可以process()方法来处理我们的逻辑。如果是Keyed Stream连接广播流,process()里面的参数需是KeyedBroadcastProcessFunction;如果是Non-Keyed Stream连接广播流,process()里面的参数是BroadcastProcessFunction

1、Keyed Stream连接广播流示例:

public class KeyedBroadcastStream {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        Properties p = new Properties();
        p.setProperty("bootstrap.servers", "localhost:9092");

        SingleOutputStreamOperator<User> user = env
                .addSource(new FlinkKafkaConsumer010<String>("user", new SimpleStringSchema(), p))
                .map((MapFunction<String, User>) value -> new Gson().fromJson(value, User.class));

        user.print("user: ");

        KeyedStream<Order, String> order = env
                .addSource(new FlinkKafkaConsumer010<String>("order", new SimpleStringSchema(), p))
                .map((MapFunction<String, Order>) value -> new Gson().fromJson(value, Order.class))
                .keyBy((KeySelector<Order, String>) value -> value.userId);

        order.print("order: ");

        MapStateDescriptor<String, User> descriptor = new MapStateDescriptor<String, User>("user", String.class, User.class);
        org.apache.flink.streaming.api.datastream.BroadcastStream<User> broadcast = user.broadcast(descriptor);

        order
                .connect(broadcast)
                .process(new KeyedBroadcastProcessFunction<String, Order, User, String>() {
                    @Override
                    public void processElement(Order value, ReadOnlyContext ctx, Collector<String> out) throws Exception {
                        ReadOnlyBroadcastState<String, User> broadcastState = ctx.getBroadcastState(descriptor);
                        // 从广播中获取对应的key的value
                        User user = broadcastState.get(value.userId);
                        if (user != null) {
                            Tuple8<String, String, String, Long, String, String, String, Long> result = new Tuple8<>(
                                    value.userId,
                                    value.orderId,
                                    value.price,
                                    value.timestamp,
                                    user.name,
                                    user.age,
                                    user.sex,
                                    user.createTime
                            );
                            String s = result.toString();
                            out.collect(s);
                        }
                    }

                    @Override
                    public void processBroadcastElement(User value, Context ctx, Collector<String> out) throws Exception {
                        BroadcastState<String, User> broadcastState = ctx.getBroadcastState(descriptor);
                        broadcastState.put(value.userId, value);
                    }
                })
                .print("");

        env.execute("broadcast: ");
    }
}

2、Non-Keyed Stream连接广播流

public class BroadcastStream {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        Properties p = new Properties();
        p.setProperty("bootstrap.servers", "localhost:9092");

        SingleOutputStreamOperator<User> user = env
                .addSource(new FlinkKafkaConsumer010<String>("user", new SimpleStringSchema(), p))
                .map(new MapFunction<String, User>() {
                    @Override
                    public User map(String value) throws Exception {
                        return new Gson().fromJson(value, User.class);
                    }
                });

        user.print("user: ");

        SingleOutputStreamOperator<Order> order = env
                .addSource(new FlinkKafkaConsumer010<String>("order", new SimpleStringSchema(), p))
                .map(new MapFunction<String, Order>() {
                    @Override
                    public Order map(String value) throws Exception {
                        return new Gson().fromJson(value, Order.class);
                    }
                });

        order.print("order: ");

        MapStateDescriptor<String, User> descriptor = new MapStateDescriptor<String, User>("user", String.class, User.class);
        org.apache.flink.streaming.api.datastream.BroadcastStream<User> broadcast = user.broadcast(descriptor);
        BroadcastConnectedStream<Order, User> connect = order.connect(broadcast);

        connect
                .process(new BroadcastProcessFunction<Order, User, String>() {
                    @Override
                    public void processElement(Order value, ReadOnlyContext ctx, Collector<String> out) throws Exception {
                        ReadOnlyBroadcastState<String, User> broadcastState = ctx.getBroadcastState(descriptor);
                        // 从广播中获取对应的key的value
                        User user = broadcastState.get(value.userId);
                        if (user != null) {
                            Tuple8<String, String, String, Long, String, String, String, Long> result = new Tuple8<>(
                                    value.userId,
                                    value.orderId,
                                    value.price,
                                    value.timestamp,
                                    user.name,
                                    user.age,
                                    user.sex,
                                    user.createTime
                            );
                            String s = result.toString();
                            out.collect(s);
                        }
                    }

                    @Override
                    public void processBroadcastElement(User value, Context ctx, Collector<String> out) throws Exception {
                        BroadcastState<String, User> broadcastState = ctx.getBroadcastState(descriptor);
                        broadcastState.put(value.userId, value);
                    }
                })
                .print("result: ");

        env.execute("broadcast: ");
    }
}
### Flink 的状态存储机制及支持的方式 Flink 是一款强大的流处理框架,其状态管理功能对于高效执行复杂流处理任务至关重要。以下是关于 Flink 状态存储机制及其支持方式的详细介绍。 #### 1. 分布式状态存储 Flink 使用分布式存储系统来保存状态数据,从而确保高可用性和可扩展性[^2]。常见的分布式存储系统包括 HDFS 和 RocksDB。这些存储系统的引入使得即使在大规模集群环境下,Flink 也能有效应对海量状态数据的需求。 #### 2. 状态类型的支持 Flink 主要支持两种类型的状态:键控状态(Keyed State)和算子状态(Operator State)。 - **键控状态**:这种状态与特定 key 关联,在分区级别上独立存在。每个 key 对应一份独立的状态副本,适用于基于 key 进行分组的操作场景[^1]。 - **算子状态**:该状态属于整个算子实例,通常用于不依赖于 key 的全局操作,比如窗口聚合或连接器中的偏移量记录。 #### 3. 特殊状态——Broadcast State 除了上述基本状态外,Flink 还提供了 Broadcast State 来满足跨并行任务间的数据广播需求。Broadcast State 将一组数据发送到所有并行任务中,使它们能够访问相同的共享数据集[^4]。然而需要注意的是,由于需要在网络上传输大量数据Broadcast State 可能会对性能造成一定影响,因此需谨慎设计使用场景。 #### 4. 状态持久化与可靠性保障 为了保证容错能力,Flink 实现了一套完整的检查点机制(Checkpointing),定期将当前运行时的状态写入可靠的分布式文件系统中作为备份。当发生故障时,可以从最近一次成功的 Checkpoint 中恢复作业继续执行。此外还有一种轻量级快照形式 Savepoints,允许手动触发以便迁移或者升级应用而无需中断服务。 #### 5. 状态缩减技术 随着长时间运行的应用程序不断积累新的输入事件,如果不加以控制,则可能导致内存占用过高甚至耗尽资源的情况出现。为此 Apache Flink 设计了几种有效的手段帮助开发者管理和压缩状态规模: - 利用时间窗函数限制保留周期内的历史信息; - 设置 TTL 参数让超过指定存活期限的部分被自动清除; - 应用增量更新模式仅保留最新变化部分而非全量复制; - 结合第三方数据库引擎如 RocksDB 提升效率同时节省空间开销等措施均有助于缓解这一问题带来的压力[^3]。 ```python # 示例代码展示如何定义带TTL配置的StateDescriptor from pyflink.datastream import StreamExecutionEnvironment from pyflink.table import EnvironmentSettings, TableEnvironment from pyflink.state import ValueStateDescriptor, TimeCharacteristic, StateTtlConfig env = StreamExecutionEnvironment.get_execution_environment() t_env = TableEnvironment.create(EnvironmentSettings.new_instance().in_streaming_mode().build()) ttl_config = StateTtlConfig \ .new_builder(Time.seconds(10)) \ .set_update_type(StateTtlConfig.UpdateType.OnCreateAndWrite) \ .disable_cleanup_in_background() \ .build() state_descriptor = ValueStateDescriptor("example_state", Types.STRING()) state_descriptor.enable_time_to_live(ttl_config) keyed_stream = env.from_collection([...]).key_by(lambda x: ...) value_state = keyed_stream.map(...).get_runtime_context().get_state(state_descriptor) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值