在 Flink 应用程序中传递和使用参数

几乎所有的 Flink 应用程序,包括批处理和流处理,都依赖于外部配置参数,这些参数被用来指定输入和输出源(如路径或者地址),系统参数(并发数,运行时配置)和应用程序的可配参数(通常用在自定义函数中)。

Flink 提供了一个简单的叫做 ParameterTool 的使用工具,提供了一些基础的工具来解决这些问题,当然你也可以不用这里所描述的ParameterTool,使用其他的框架,如:Commons CLI 和 argparse4j 在 Flink 中也是支持的。

一、获取配置值,并传入 PratameterTool。

ParameterTool 提供了一系列预定义的静态方法来读取配置信息,ParameterTool 内部是一个 Map<String, String>,所以很容易与你自己的配置形式相集成。

1、从命令行中获取。

public static void main(String[] args) throws Exception {
   ParameterTool parameter = ParameterTool.fromArgs(args);
   ……
}   

在执行命令中使用:--name 张三 --age 20

2、从 properties 文件中获取。

// kafka.properties
ParameterTool parameter = ParameterTool.fromPropertiesFile(Constant.CONFIG_NAME_KAFKA);

3、从系统属性中获取。

当启动一个JVM时,你可以给它传递一些系统属性,如:-Dinput=hdfs:///mydata,你可以用这些系统属性来初始化 PrameterTool

ParameterTool parameter = ParameterTool.fromSystemProperties();

二、在程序中,使用 ParameterTool 参数。

1、直接从 ParameterTool 中获取,使用。

ParameterTool parameter = ParameterTool.fromPropertiesFile(Constant.CONFIG_NAME_KAFKA);
parameter.getNumberOfParameters(); // 参数个数
String topic = parameter.getRequired("kafka.topic");
String key = parameter.get("kafka.key", "test");
int port = parameter.getInt("kafka.port", 5672);

// kafka 配置信息
Properties properties = new Properties();
properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, groupId);
properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, reset);

// kafka consumer
FlinkKafkaConsumer consumer = new FlinkKafkaConsumer(topic, new SimpleStringSchema(), properties);

2、因为 ParameterTool 是可序列化的,可将 ParameterToole 传递给函数使用。

// 数据处理
sourceStream
        .connect(configBroadcastStream)
        .process(new KafkaUserOpinionBroadcastProcessFunction(parameterTool))
        .uid("broadcast-connect-process");
public class KafkaUserOpinionBroadcastProcessFunction extends KeyedBroadcastProcessFunction<Tuple, UserOpinionData, SensitiveWordConfig, String> implements Serializable {
    private static final long serialVersionUID = 10000L;

    // 参数信息
    private Map<String, String> globalJobParametersMap;

    public KafkaUserOpinionBroadcastProcessFunction(ParameterTool parameterTool) {
        this.globalJobParametersMap = parameterTool.toMap();
    }
    ……
}

3、将 ParameterTool 注册为全局参数使用。

在 ExecutionConfig 中注册为全作业参数的参数,可以被 JobManager 的 web 端以及用户所有自定义函数中以配置值的形式访问。

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
ParameterTool parameter = ParameterTool.fromPropertiesFile(Constant.CONFIG_NAME_KAFKA);
env.getConfig().setGlobalJobParameters(parameter);
static final class UserOpinionFilter extends RichFlatMapFunction<UserOpinionData, Tuple2<String, Integer>> {
        ParameterTool parameterTool;

        @Override
        public void open(Configuration parameters) throws Exception {
            parameterTool = (ParameterTool) getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
        }

        @Override
        public void flatMap(UserOpinionData userOpinionData, Collector<Tuple2<String, Integer>> collector) throws Exception {

        }
    }

该方法使用不当,可能会造成 flink on yarn 任务提交不了,如

[root@snd-gp2-slave bin]# ./flink run -m yarn-cluster -yn 1 -p 4 -yjm 1024 -ytm 4096 -ynm FlinkOnYarnSession-UserOpinionDataConsumer -d -c com.igg.flink.tool.userOpinionMonitor.kafka.consumer.JavaKafkaUserOpinionDataConsumer /home/flink/igg-flink-tool-1.0.0-SNAPSHOT.jar
2020-01-08 22:31:05,143 INFO  org.apache.flink.yarn.cli.FlinkYarnSessionCli                 - No path for the flink jar passed. Using the location of class org.apache.flink.yarn.YarnClusterDescriptor to locate the jar
2020-01-08 22:31:05,143 INFO  org.apache.flink.yarn.cli.FlinkYarnSessionCli                 - No path for the flink jar passed. Using the location of class org.apache.flink.yarn.YarnClusterDescriptor to locate the jar

或者 checkpoint 时间很长。

三、使用distributedCache

parametertool 进行参数传递会很方便,但是也仅仅适用于少量参数的传递,如果有比较大量的数据传递,flink则提供了另外的方

式来进行,其中之一即是 distributedCache。

在定义DAG图的时候指定缓存文件

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// Register a file from HDFS
env.registerCachedFile("hdfs:///path/to/file", "sensitiveInfo");

flink本身支持指定本地的缓存文件,但一般而言,建议指定分布式存储,如hdfs上的文件,并为其指定一个名称。

使用起来也很简单,继承Rich函数,在open方法中进行获取。

// 定义文件缓存变量
private File sensitiveInfo = null;

@Override
public void open(Configuration parameters) throws Exception {
    sensitiveInfo = getRuntimeContext().getDistributedCache().getFile("sensitiveInfo");
}

应该说定义的缓存本身都是固定的,缓存不会变化,那么如果缓存本身随着时间也会发生变化,怎么办?

那就用connectStream,其实也是流的聚合了。

四、使用connectStream

这个也是在其他计算引擎中广泛使用的方法之一。

使用 ConnectedStream 的前提当然是需要有一个动态的流,比如在主数据之外,还有一些规则数据,这些规则数据会通过

Restful服务来发布。

// 广播流
BroadcastStream configBroadcastStream = configStream
        .map((MapFunction<String, SensitiveWordConfig>) s -> SensitiveWordConfig.buildSensitiveWordConfig(s))
        .filter((FilterFunction<SensitiveWordConfig>) sensitiveWordConfig -> sensitiveWordConfig != null)
        .uid("sensitive-word-source")
        .broadcast(new MapStateDescriptor(
                "user-opinion-broadcast-state-desc",
            BasicTypeInfo.STRING_TYPE_INFO,
            TypeInformation.of(new TypeHint<SensitiveWordConfig>(){})
        ));

具体的使用代码,可以看笔者的博文 基于Kafka+Flink+Hutool的用户言论实时监控案例

对于 ConnectedStream,数据是从 JM 发送到 TM,有时我们需要将数据从 TM发送到 JM,要如何实现呢?可以使用

accumulator。

flink提供了accumulator来实现数据的回传,亦即从 TM 传回到 JM。

flink本身提供了一些内置的accumulator:

  • IntCounterLongCounterDoubleCounter – allows summing together int, long, double values sent from task managers
  • AverageAccumulator – calculates an average of double values
  • LongMaximumLongMinimumIntMaximumIntMinimumDoubleMaximumDoubleMinimum – accumulators to determine maximum and minimum values for different types
  • Histogram – used to computed distribution of values from task managers

首先需要定义一个accumulator,然后在某个自定义函数中来注册它,这样在客户端就可以获取相应的的值。

new RichFlatMapFunction<String, Tuple2<String, Integer>>() {

    // Create an accumulator
    private IntCounter linesNum = new IntCounter();

    @Override
    public void open(Configuration parameters) throws Exception {
        // Register accumulator
        getRuntimeContext().addAccumulator("linesNum", linesNum);
    }

    @Override
    public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
        String[] words = line.split("\\W+");
        for (String word : words) {
            out.collect(new Tuple2<>(word, 1));
        }
        
        // Increment after each line is processed
        linesNum.add(1);
    }
}

在定义DAG中获取回传数据 

public static void main(String[] args) throws Exception {
  // todo:
  
  // Get accumulator result
  int linesNum = env.getLastJobExecutionResult().getAccumulatorResult("linesNum");
  System.out.println(linesNum);
  
  env.execute();
}

上面介绍了几种参数传递的方式,在日常的使用中,可能不仅仅是使用其中一种,或许是某些的组合,比如通过parametertool来

传递hdfs的路径,再通过filecache来读取缓存。

如果有写的不对的地方,欢迎大家指正。有什么疑问,欢迎加QQ群:176098255

发布了82 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/magic_kid_2010/article/details/103902067
今日推荐