Flink使用广播实现配置动态更新

本着开源的精神将学习成果分享,转载请注明出处。

问题复现

场景

对每条流数据进行关键字检测,对符合条件的消息进行拦截。例如关键字是 java,则消息 java是世界上最优秀的语言就会被拦截。

需求

拦截的关键字不一定是 java,可能需要变更拦截关键词,例如: php。因此关键字必须做到是可配置的。

问题

我们首先想到的是存在数据库或外部传入参数。但又因为该关键词是在算子中作为一个变量,一旦作业启动,想修改关键字不得不停掉作业,然后再重新启动作业。繁琐且不友好,有没有什么可以动态的修改算子里变量的方法?

解决方案

没错,使用广播的方式去解决。画了2张图表示了他们的区别


Flink使用广播实现配置动态更新_第1张图片
算子处理广播流方式
Flink使用广播实现配置动态更新_第2张图片
算子处理数据流方式

广播和普通的流数据不同的是:广播流的1条流数据能够被算子的所有分区所处理,而数据流的1条流数据只能够被算子的某一分区处理。因此广播流的特点也决定适合做配置的动态更新

源码

package com.example.flink;

import org.apache.flink.api.common.state.MapStateDescriptor;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
import org.apache.flink.configuration.Configuration;
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.functions.co.BroadcastProcessFunction;
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
import org.apache.flink.util.Collector;

import java.util.concurrent.TimeUnit;

/**
 * 作者(author):miao
 * 时间(date): 2019-04-01 15:17
 * 功能描述(description):使用广播流实现配置的动态更新
 */
public class BroadcastStreamDemo {

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

        // 构建流处理环境
        final StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();

        // 配置处理环境的并发度为4
        environment.setParallelism(4);

        final MapStateDescriptor CONFIG_KEYWORDS = new MapStateDescriptor<>(
                "config-keywords",
                BasicTypeInfo.STRING_TYPE_INFO,
                BasicTypeInfo.STRING_TYPE_INFO);

        // 自定义广播流(单例)
        BroadcastStream broadcastStream = environment.addSource(new RichSourceFunction() {

            private volatile boolean isRunning = true;
            //测试数据集
            private String[] dataSet = new String[] {
                    "java",
                    "swift",
                    "php",
                    "go",
                    "python"
            };

            /**
             * 数据源:模拟每30秒随机更新一次拦截的关键字
             * @param ctx
             * @throws Exception
             */
            @Override
            public void run(SourceContext ctx) throws Exception {
                int size = dataSet.length;
                while (isRunning) {
                    TimeUnit.SECONDS.sleep(30);
                    int seed = (int) (Math.random() * size);
                    //随机选择关键字发送
                    ctx.collect(dataSet[seed]);
                    System.out.println("读取到上游发送的关键字:" + dataSet[seed]);
                }
            }

            @Override
            public void cancel() {
                isRunning = false;
            }
        }).setParallelism(1).broadcast(CONFIG_KEYWORDS);

        // 自定义数据流(单例)
        DataStream dataStream = environment.addSource(new RichSourceFunction() {

            private volatile boolean isRunning = true;

            //测试数据集
            private String[] dataSet = new String[] {
                    "java是世界上最优秀的语言",
                    "swift是世界上最优秀的语言",
                    "php是世界上最优秀的语言",
                    "go是世界上最优秀的语言",
                    "python是世界上最优秀的语言"
            };

            /**
             * 模拟每3秒随机产生1条消息
             * @param ctx
             * @throws Exception
             */
            @Override
            public void run(SourceContext ctx) throws Exception {
                int size = dataSet.length;
                while (isRunning) {
                    TimeUnit.SECONDS.sleep(3);
                    int seed = (int) (Math.random() * size);
                    ctx.collect(dataSet[seed]);
                    System.out.println("读取到上游发送的消息:" + dataSet[seed]);
                }
            }

            @Override
            public void cancel() {
                isRunning = false;
            }

        }).setParallelism(1);

        // 数据流和广播流连接处理并将拦截结果打印
        dataStream.connect(broadcastStream).process(new BroadcastProcessFunction() {

            //拦截的关键字
            private String keywords = null;

            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);
                keywords = "java";
                System.out.println("初始化模拟连接数据库读取拦截关键字:java");
            }

            @Override
            public void processElement(String value, ReadOnlyContext ctx, Collector out) throws Exception {
                if (value.contains(keywords)) {
                    out.collect("拦截消息:" + value + ", 原因:包含拦截关键字:" + keywords);
                }
            }

            @Override
            public void processBroadcastElement(String value, Context ctx, Collector out) throws Exception {
                keywords = value;
                System.out.println("关键字更新成功,更新拦截关键字:" + value);
            }
        }).print();

        // 懒加载执行
        environment.execute();
    }

}

pom.xml



    4.0.0

    com.example.flink
    broadcast-stream-demo
    1.0-SNAPSHOT
    jar

    
        UTF-8
        1.7.1
        1.8
        2.11
    

    
        
            org.apache.flink
            flink-java
            ${flink.version}
        
        
            org.apache.flink
            flink-streaming-java_${scala.version}
            ${flink.version}
        
        
            org.slf4j
            slf4j-nop
            1.7.15
        
    

    
        
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    
                    ${java.version}
                    
                    ${java.version}
                
            
            
                org.apache.maven.plugins
                maven-shade-plugin
                2.4.1
                
                    
                        package
                        
                            shade
                        
                        
                            
                                
                                    com.example.flink.BroadcastStreamDemo
                                
                            
                        
                    
                
            
        
    


运行结果

初始化模拟连接数据库读取拦截关键字:java
初始化模拟连接数据库读取拦截关键字:java
初始化模拟连接数据库读取拦截关键字:java
初始化模拟连接数据库读取拦截关键字:java
读取到上游发送的消息:java是世界上最优秀的语言
2> 拦截消息:java是世界上最优秀的语言, 原因:包含拦截关键字:java
读取到上游发送的消息:go是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
读取到上游发送的消息:swift是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
读取到上游发送的消息:java是世界上最优秀的语言
4> 拦截消息:java是世界上最优秀的语言, 原因:包含拦截关键字:java
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:java是世界上最优秀的语言
2> 拦截消息:java是世界上最优秀的语言, 原因:包含拦截关键字:java
读取到上游发送的关键字:php
读取到上游发送的消息:php是世界上最优秀的语言
关键字更新成功,更新拦截关键字:php
关键字更新成功,更新拦截关键字:php
关键字更新成功,更新拦截关键字:php
关键字更新成功,更新拦截关键字:php
3> 拦截消息:php是世界上最优秀的语言, 原因:包含拦截关键字:php
读取到上游发送的消息:go是世界上最优秀的语言
读取到上游发送的消息:go是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
3> 拦截消息:php是世界上最优秀的语言, 原因:包含拦截关键字:php
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:go是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的关键字:swift
关键字更新成功,更新拦截关键字:swift
关键字更新成功,更新拦截关键字:swift
关键字更新成功,更新拦截关键字:swift
关键字更新成功,更新拦截关键字:swift
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
读取到上游发送的消息:go是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:php是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:java是世界上最优秀的语言
读取到上游发送的消息:python是世界上最优秀的语言
读取到上游发送的消息:swift是世界上最优秀的语言
1> 拦截消息:swift是世界上最优秀的语言, 原因:包含拦截关键字:swift
读取到上游发送的消息:python是世界上最优秀的语言

参考资料

聊聊flink的Broadcast State

你可能感兴趣的:(Flink使用广播实现配置动态更新)