flink.11 DataStream api之广播

概念

广播意思就是将变量发送到每一个并行运行的task所在的机器上,这样可以避免数据在涉及到聚合之时的跨网传输,提高流运行的速度,正因为广播是提前将需要的公共数据发送到各个集群的节点上,所以来说,广播不适合广播大量的数据.

实现广播的步骤

  1. 构造数据流A, B 这里假设B为要被广播的流数据,A为普通数据流,A需要用B流做一些逻辑运算
  2. 为广播流构造描述符对象
> MapStateDescriptor<String, Rule> ruleStateDescriptor = new
> MapStateDescriptor<>( 			"RulesBroadcastState",
> 			BasicTypeInfo.STRING_TYPE_INFO, 			TypeInformation.of(new
> TypeHint<Rule>() {}));
  1. env调用广播方法 broadcase()
  2. 非广播流调用connect(广播流) 返回合并流对象,合并流对象调用 process(自己实现的接口), 接口中定义处理逻辑
DataStream<String> output = colorPartitionedStream
                 .connect(ruleBroadcastStream)
                 .process(
                     
                     // type arguments in our KeyedBroadcastProcessFunction represent: 
                     //   1. the key of the keyed stream
                     //   2. the type of elements in the non-broadcast side
                     //   3. the type of elements in the broadcast side
                     //   4. the type of the result, here a string
                     
                     new KeyedBroadcastProcessFunction<Color, Item, Rule, String>() {
                         // my matching logic
                     }
                 );
 

5.编写处理逻辑,处理逻辑有两个接口,BroadcastProcessFunction 和 KeyedBroadcastProcessFunction ,前者用于处理非键控流,后者用于处理键控流. 每个接口都有两个核心的方法:processElement,和processBroadcastElement, processBroadcastElement用于处理接收到的广播流,一般来说会调用ctx.getBroadcastState(mapStateDescriptor);
broadcastState.put(value, value); 意思是从数据源B流中获取新订阅到的广播流数据,然后填充到全局的广播流中. processElement主要用于获取广播流,然后获取数据流,然后定义自己的处理逻辑,处理完了之后发送到下游

例子

主类:

 StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();
        environment.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime);
        environment.enableCheckpointing(1000 * 180);
        FlinkKafkaConsumer010<String> location = KafkaUtil.getConsumer("event_stream", "test_1", "test");
        FlinkKafkaConsumer010<String> object = KafkaUtil.getConsumer("bro_stream", "test_2", "test");
        // 把事件流按key进行分流,这样相同的key会发到同一个节点
        KeyedStream<People, String> driverDatastream = environment.addSource(location).map(new MapFunction<String, Driver>() {

            @Override
            public People map(String s) throws Exception {
                return parse(s);
            }
        }).keyBy((KeySelector<People, String>) people -> people.id);
    
        // 描述这个map ,key value都为string 
        MapStateDescriptor<String, String> mapStateDescriptor = new MapStateDescriptor<String, String>("register", Types.STRING, Types.STRING);
        BroadcastStream<String> broadcast = environment.addSource(object).broadcast(mapStateDescriptor);
        driverDatastream.connect(broadcast).process(new PatternEvaluator()).print();
        try {
            environment.execute("register collect");
        } catch (Exception e) {
            e.printStackTrace();
        }

处理类

因为主类中用了键控流,(所谓键控流就是根据key select 对流数据进行分区,相同key的数据会发送到同一个线程中处理),所以要用接口KeyedBroadcastProcessFunction

public class PatternEvaluator extends KeyedBroadcastProcessFunction<String, People, String, People> {

    MapStateDescriptor<String, String> mapStateDescriptor;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        // 这里需要初始化map state 描述
        mapStateDescriptor = new MapStateDescriptor<String, String>("register", Types.STRING, Types.STRING);

    }

    // 处理每一个元素,看state是否有匹配的,有的话,下发到下一个节点
    @Override
    public void processElement(People value, ReadOnlyContext ctx, Collector<People> out) throws Exception {
        ReadOnlyBroadcastState<String, String> broadcastState = ctx.getBroadcastState(mapStateDescriptor);
        if ((value.getIdCard() != null && broadcastState.get(value.getIdCard()) != null) || (value.getPhone() != null && broadcastState.get(value.getPhone()) != null)) {
            System.out.println("匹配到" + value.toString());
            out.collect(value);
        }

    }


    // 新增加的广播元素,放入state中
    @Override
    public void processBroadcastElement(String value, Context ctx, Collector<People> out) throws Exception {
        System.out.println("新增加需要监控的" + value.toString());
        BroadcastState<String, String> broadcastState = ctx.getBroadcastState(mapStateDescriptor);
        broadcastState.put(value, value);
    }
}

代码部分参考:广播
附上官网:官网

在Apache Flink中,读取配置文件并进行广播是一种常见的模式,用于将配置信息全局化地提供给Flink作业中的各个算子使用。以下是使用Java API实现这一模式的一个基本示例: 1. 首先,创建一个配置文件(比如`config.properties`),并将其放在合适的路径下。在这个文件中,你可以定义需要全局访问的配置项,例如: ``` key1=value1 key2=value2 ``` 2. 接下来,在Flink作业中读取这个配置文件,并创建一个广播状态(Broadcast State)。在Flink中,广播状态允许你将数据(如配置信息)广播到每个并行实例上,使得每个实例都能够访问。 3. 实现代码示例: ```java // 导入相关的Flink类和函数 import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.state.BroadcastState; import org.apache.flink.api.common.state.MapStateDescriptor; import org.apache.flink.api.common.state.BroadcastState; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.BroadcastStream; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.datastream.BroadcastStream; import org.apache.flink.streaming.api.functions.co.BroadcastProcessFunction; import org.apache.flink.util.Collector; // 创建Flink的流执行环境 final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // 读取配置文件路径 Properties properties = new Properties(); properties.setProperty("path", "path/to/your/config.properties"); // 将配置文件加载到配置对象中 // 创建一个广播状态描述符 final MapStateDescriptor<Void, Properties> broadcastStateDescriptor = new MapStateDescriptor<>( "config", Void.class, Properties.class); // 创建一个普通的数据流(这里以sourceFunction作为数据源) DataStream<Tuple2<String, String>> streamSource = env.addSource(new sourceFunction()); // 创建广播状态流 BroadcastStream<Properties> broadcastStream = env.fromElements(properties) .broadcast(broadcastStateDescriptor); // 将普通数据流和广播流连接起来 DataStream<Tuple2<String, String>> resultStream = streamSource.connect(broadcastStream) .process(new BroadcastProcessFunction<Tuple2<String, String>, Properties, Object>() { @Override public void processElement(Tuple2<String, String> value, ReadOnlyContext ctx, Collector<Object> out) throws Exception { // 从只读上下文中获取广播状态 Properties config = ctx.getBroadcastState(broadcastStateDescriptor).get(null); // 使用配置信息处理数据,输出结果 out.collect(new Object()); } @Override public void processBroadcastElement(Properties value, Context ctx, Collector<Object> out) throws Exception { // 当接收到新的配置信息时,更新广播状态 ctx.getBroadcastState(broadcastStateDescriptor).put(null, value); } }); // 执行Flink作业 env.execute("Broadcast Flink Job"); ``` 在上面的代码中,我们定义了一个`BroadcastProcessFunction`,它有两个方法:`processElement`用于处理普通数据流中的每个元素,`processBroadcastElement`用于处理广播数据流中的每个元素。在这个例子中,我们将配置信息保存在了`ReadOnlyContext`的广播状态中,之后就可以在`processElement`方法中访问并使用这些信息了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我先森

鼓励一个吧,哈哈

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

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

打赏作者

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

抵扣说明:

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

余额充值