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

本文详细介绍了Apache Flink中参数配置与传递的各种方式,包括使用ParameterTool从命令行、properties文件和系统属性中获取配置,以及在程序中使用ParameterTool参数的方法。此外,还探讨了如何使用distributedCache和connectStream进行大量数据传递,以及如何利用accumulator实现数据从TaskManager回传到JobManager。

几乎所有的 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来读取缓存。

一起学习

在 Apache Flink 应用程序传递使用日期参数,通常涉及两种场景:一种是作为作业的输入数据进行处理,另一种是作为作业配置的一部分用于控制逻辑或行为。以下是一些常见的方法实现方式: ### 1. 将日期作为输入数据处理 如果日期参数是流式数据中的一个字段,可以直接将其作为事件时间戳用于窗口操作、状态管理等。 例如,假设有一个包含日期字段的数据流,可以通过 `assignTimestampsAndWatermarks` 方法提取时间戳并设置水印: ```java DataStream<Event> stream = env.addSource(new FlinkKafkaConsumer<>("topic", new SimpleStringSchema(), properties)) .map(new MapFunction<String, Event>() { @Override public Event map(String value) throws Exception { // 解析 JSON 或其他格式,并返回包含时间戳的对象 return parseEvent(value); } }) .assignTimestampsAndWatermarks(WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(10)) .withTimestampAssigner((event, timestamp) -> event.getTimestamp())); ``` 在此示例中,`Event` 类需要确保其字段可序列化,并且能够正确解析日期字段[^4]。 ### 2. 使用日期参数作为作业配置 Flink 支持通过 `ParameterTool` 来传递命令行参数或从配置文件加载参数。可以将日期参数作为字符串传递,并在运行时转换为 Java 的 `LocalDate` 或 `Date` 对象。 例如,启动 Flink 作业时传递日期参数: ```bash flink run -D-Djob.date=2023-10-01 my_job.jar ``` 在代码中获取并解析该日期: ```java ParameterTool params = ParameterTool.fromArgs(args); String dateString = params.get("job.date"); LocalDate date = LocalDate.parse(dateString); // 默认格式 yyyy-MM-dd ``` 此外,也可以通过 `Configuration` 对象传递参数到算子中,以支持更复杂的分布式场景。 ### 3. 在 SQL 查询中使用日期参数 如果使用 Flink SQL,可以在查询中动态传入日期参数。例如,在 Table API 中绑定参数: ```java Table table = tEnv.fromDataStream(stream, Schema.newBuilder() .column("name", "STRING") .column("timestamp", "TIMESTAMP(3)") .build()); tEnv.createTemporaryView("my_table", table); // 绑定参数并执行查询 Table result = tEnv.sqlQuery("SELECT * FROM my_table WHERE timestamp > '" + date + "'"); ``` Flink SQL 还支持更高级的参数化查询机制,可以结合 `PreparedStatement` 等方式来安全地传递日期值。 ### 4. 使用 Catalog 持久化日期相关元数据 如果日期参数与外部系统(如 Hive)的分区有关,可以通过 `HiveCatalog` 获取表的分区信息,并基于此构建动态过滤条件。例如,读取 Hive 表的某个特定日期分区: ```java tEnv.useCatalog("hive_catalog"); tEnv.useDatabase("default"); // 假设 Hive 表有按天分区的字段 dt Table hiveTable = tEnv.from("my_hive_table").filter("dt = '2023-10-01'"); ``` 这样可以利用 Catalog 提供的元数据信息,动态决定要处理哪些日期范围的数据[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

magic_kid_2010

你的支持将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值