最佳实践
此页面包含Flink程序员有关如何解决常见问题的最佳实践的集合。
- 解析命令行参数并在Flink应用程序中传递它们
- 命名大型TupleX类型
- 使用Logback代替Log4j
解析命令行参数并在Flink应用程序中传递它们
几乎所有Flink应用程序(批处理和流式处理)都依赖于外部配置参数。它们用于指定输入和输出源(如路径或地址),系统参数(并行性,运行时配置)和特定于应用程序的参数(通常在用户功能内使用)。
Flink提供了一个简单的实用程序,称为ParameterTool
提供解决这些问题的一些基本工具。请注意,您不必使用ParameterTool
此处描述的内容。其他框架(例如Commons CLI和 argparse4j)也可以与Flink一起使用。
将您的配置值放入 ParameterTool
所述ParameterTool
用于读取所述配置提供一组预定义的静态方法。该工具在内部期望使用Map<String, String>
,因此将其与您自己的配置样式集成起来非常容易。
来自.properties
文件
以下方法将读取属性文件并提供键/值对:
String propertiesFilePath = "/home/sam/flink/myjob.properties";
ParameterTool parameter = ParameterTool.fromPropertiesFile(propertiesFilePath);
File propertiesFile = new File(propertiesFilePath);
ParameterTool parameter = ParameterTool.fromPropertiesFile(propertiesFile);
InputStream propertiesFileInputStream = new FileInputStream(file);
ParameterTool parameter = ParameterTool.fromPropertiesFile(propertiesFileInputStream);
从命令行参数
这样可以--input hdfs:///mydata --elements 42
从命令行获取参数。
public static void main(String[] args) {
ParameterTool parameter = ParameterTool.fromArgs(args);
// .. regular code ..
从系统属性
启动JVM时,可以将系统属性传递给它:-Dinput=hdfs:///mydata
。您还可以ParameterTool
通过以下系统属性初始化:
ParameterTool parameter = ParameterTool.fromSystemProperties();
在Flink程序中使用参数
现在我们已经从某个地方获得了参数(见上文),我们可以通过各种方式使用它们。
直接从 ParameterTool
在ParameterTool
本身具有用于访问值的方法。
ParameterTool parameters = // ...
parameter.getRequired("input");
parameter.get("output", "myDefaultValue");
parameter.getLong("expectedCount", -1L)ja;
parameter.getNumberOfParameters()
// .. there are more methods available.
您可以直接在main()
提交应用程序的客户端方法中使用这些方法的返回值。例如,您可以像这样设置运算符的并行性:
ParameterTool parameters = ParameterTool.fromArgs(args);
int parallelism = parameters.get("mapParallelism", 2);
DataSet<Tuple2<String, Integer>> counts = text.flatMap(new Tokenizer()).setParallelism(parallelism);
由于ParameterTool
可以序列化,因此您可以将其传递给函数本身:
ParameterTool parameters = ParameterTool.fromArgs(args);
DataSet<Tuple2<String, Integer>> counts = text.flatMap(new Tokenizer(parameters));
然后在函数内部使用它从命令行获取值。
全局注册参数
ExecutionConfig
可以从JobManager Web界面和用户定义的所有功能中将其注册为全局作业参数的参数作为配置值进行访问。
全局注册参数:
ParameterTool parameters = ParameterTool.fromArgs(args);
// set up the execution environment
final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
env.getConfig().setGlobalJobParameters(parameters);
通过任何丰富的用户功能访问它们:
public static final class Tokenizer extends RichFlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
ParameterTool parameters = (ParameterTool)
getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
parameters.getRequired("input");
// .. do more ..
命名大型TupleX类型
建议使用POJO(普通的旧Java对象),而不要TupleX
使用具有许多字段的数据类型。同样,POJO可以用来给大型Tuple
类型起一个名字。
例
而不是使用:
Tuple11<String, String, ..., String> var = new ...;
创建从大Tuple类型扩展的自定义类型要容易得多。
CustomType var = new ...;
public static class CustomType extends Tuple11<String, String, ..., String> {
// constructor matching super
}
使用Logback代替Log4j
注意:本教程从Flink 0.10开始适用
Apache Flink使用slf4j作为代码中的日志记录抽象。建议用户在其用户功能中也使用sfl4j。
Sfl4j是一个编译时日志记录接口,可以在运行时使用不同的日志记录实现,例如log4j或Logback。
Flink默认情况下取决于Log4j。本页介绍如何将Flink与Logback结合使用。用户报告说,他们还可以使用本教程使用Graylog来设置集中式日志记录。
要在代码中获取记录器实例,请使用以下代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass implements MapFunction {
private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
// ...
在IDE中/从Java应用程序中运行Flink时使用Logback
在使用依赖管理器(例如Maven)创建的类路径执行类的所有情况下,Flink都会将log4j拉入类路径。
因此,您将需要从Flink的依赖项中排除log4j。下面的描述将假定从Flink快速入门创建的Maven项目。
pom.xml
像这样更改您的项目文件:
<dependencies>
<!-- Add the two required logback dependencies -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<!-- Add the log4j -> sfl4j (-> logback) bridge into the classpath
Hadoop is logging to log4j! -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.9.0</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>1.9.0</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>1.9.0</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
本<dependencies>
节中进行了以下更改:
log4j
从所有Flink依赖关系中排除所有依赖关系:这会使Maven忽略Flink对log4j的传递依赖关系。slf4j-log4j12
从Flink的依赖项中排除工件:由于我们将使用slf4j进行logback绑定,因此我们必须删除slf4j到log4j绑定。- 添加Logback依赖项:
logback-core
和logback-classic
- 为添加依赖项
log4j-over-slf4j
。log4j-over-slf4j
是一种工具,它允许直接使用Log4j API的旧应用程序使用Slf4j接口。Flink依赖于Hadoop,后者直接使用Log4j进行日志记录。因此,我们需要将所有记录器调用从Log4j重定向到Slf4j,后者又记录到Logback。
请注意,您需要手动将排除项添加到要添加到pom文件的所有新Flink依赖项中。
您可能还需要检查其他(非Flink)依赖项是否正在拉入log4j绑定中。您可以使用来分析项目的依赖关系mvn dependency:tree
。
在群集上运行Flink时使用Logback
当在YARN上或作为独立群集运行Flink时,本教程适用。
为了将Logback代替Log4j与Flink一起使用,您需要从目录中删除log4j-1.2.xx.jar
和。sfl4j-log4j12-xxx.jar``lib/
接下来,您需要将以下jar文件放入lib/
文件夹:
logback-classic.jar
logback-core.jar
log4j-over-slf4j.jar
:需要在类路径中存在此桥,以便将日志记录调用从Hadoop(正在使用Log4j)重定向到Slf4j。
请注意,lib/
在使用按作业的YARN群集时,需要显式设置目录。
使用自定义记录器在YARN上提交Flink的命令是: ./bin/flink run -yt $FLINK_HOME/lib <... remaining arguments ...>