Apache Flink是存在的最通用的数据流开源解决方案之一。 它支持典型批处理系统的所有主要功能,例如SQL,Hive连接器,分组依据等,同时提供容错和一次精确的语义。 因此,您可以使用它创建大量基于推送的应用程序。
但是,Apache Flink的主要缺点之一是无法修改程序的检查点状态。 首先让我明白我的意思。
检查点
Flink通过使用称为检查点的机制来提供容错功能。 它会定期为程序的所有有状态操作员/功能创建快照,并将其存储在高度持久的存储中,例如HDFS。
检查点允许Flink程序从该快照恢复。 如果由于某些错误(例如,未处理的简单异常或YARN / Mesos / k8s群集中的数据节点丢失)而导致失败,这将很有帮助。
该快照以二进制格式存储,只有Flink可以理解,这使得在重新启动之前很难修改状态。
为什么需要修改数据?
在很多情况下,您可能只需要来自检查点的部分数据,而您可能想要更新其他数据。 一个示例工作是
从一个Kafka主题中读取数值数据在1小时的窗口内聚合使用存储在操作员状态下的config提供的一些统计阈值进行分类。
public class TestCheckpointJob {
public static void main (String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime);
Properties kafkaConsumerProperties = new Properties();
kafkaConsumerProperties.setProperty( "bootstrap.servers" , "localhost:9092" );
kafkaConsumerProperties.setProperty( "group.id" , "test_group_id" );
ObjectMapper objectMapper = new ObjectMapper();
FlinkKafkaConsumer010<String> kafkaConsumer010 = new FlinkKafkaConsumer010<>( "test_topic" , new SimpleStringSchema(), kafkaConsumerProperties);
DataStream<String> kafkaSource = env.addSource(kafkaConsumer010).name( "kafka_source" ).uid( "kafka_source" );
DataStream<TestData> aggregatedStream = kafkaSource
.map(row -> objectMapper.readValue(row, TestData.class))
.keyBy(TestData::getKey)
.timeWindow(Time.hours( 1 ))
.reduce((rowA, rowB) -> {
TestData result = new TestData();
result.setKey(rowA.getKey());
result.setValue(rowA.getValue() + rowB.getValue());
result.setCreatedAt(System.currentTimeMillis());
return result;
}).name( "aggregate_stream" ).uid( "aggregate_stream" );
DataStream<LabeledTestData> labeledTestDataDataStream = aggregatedStream.keyBy(TestData::getKey).flatMap( new ClassifyData()).name( "classify_data" ).uid( "classify_data" );
labeledTestDataDataStream.map(row -> objectMapper.writeValueAsString(row)).print();
env.execute();
}
}
class ClassifyData extends RichFlatMapFunction < TestData , LabeledTestData > {
ValueState<Integer> threshold;
@Override
public void open (Configuration parameters) throws Exception {
super .open(parameters);
threshold = getRuntimeContext().getState( new ValueStateDescriptor<Integer>( "thresholdState" , Integer.class));
}
@Override
public void flatMap (TestData testData, Collector<LabeledTestData> collector) throws Exception {
LabeledTestData labeledTestData = new LabeledTestData();
labeledTestData.setKey(testData.getKey());
labeledTestData.setValue(testData.getValue());
labeledTestData.setCreatedAt(testData.getCreatedAt());
String label = "UNCLASSIFIED" ;
if (threshold.value() != null ){
label = (testData.getValue() > threshold.value()) ? "L1" : "L2" ;
}
labeledTestData.setLabel(label);
collector.collect(labeledTestData);
}
}
假设您的工作被杀死,现在您想使用检查点重新启动它,但是您还需要修改配置。 早先,除了等待作业以旧配置开始并使用来自Kafka或文件系统的流覆盖它之外,没有其他方法可以这样做。
但是,现在,您可以使用新的API轻松地做到这一点。 让我们修改上面的例子。
引导状态
以下是引导您的状态所需的必要步骤
添加依赖
< dependency >
< groupId > org.apache.flink </ groupId >
< artifactId > flink-state-processor-api_2.11 </ artifactId >
< version > 1.9.0 </ version >
</ dependency >
它不包括在默认的Flink依赖项中,需要单独添加到pom.xml文件中。
创建一个Bootstrap函数
class ConfigBootstrapper extends KeyedStateBootstrapFunction < String , TestConfig > {
ValueState<Integer> threshold;
@Override
public void open (Configuration parameters) throws Exception {
threshold = getRuntimeContext().getState( new ValueStateDescriptor<Integer>( "thresholdState" , Integer.class));
}
@Override
public void processElement (TestConfig testConfig, Context context) throws Exception {
threshold.update(testConfig.getThresholdValue());
}
}
此函数告诉Flink接收数据时要更新什么状态。 在此示例中,我们将使用收集的TestConfig数据更新阈值状态。
流动配置数据
BootstrapTransformation<TestConfig> getConfigTransformation (ExecutionEnvironment executionEnvironment) {
TestConfig testConfig = new TestConfig();
testConfig.setKey( "global" );
testConfig.setThresholdValue( 10 );
DataSet<TestConfig> configDataSet = executionEnvironment.fromElements(testConfig);
BootstrapTransformation<TestConfig> transformation = OperatorTransformation
.bootstrapWith(configDataSet)
.keyBy(TestConfig::getKey)
.transform( new ConfigBootstrapper());
return transformation;
}
现在,您需要传输配置数据。 Flink状态处理器API与数据集API无缝协作。 这并不意味着您不能在Stream环境中使用引导程序。 只是引导数据只能使用Batch API加载。 您可以在单个作业中创建批处理和流环境。
在这里,我刚刚创建了一个配置对象,然后在其上面创建了一个数据集。 然后,我们创建一个转换。 它指定要与Bootstrap Function一起使用的数据集。
更新保存点
public class TestCheckpointJob {
public static void main (String[] args) throws Exception {
bootstrapConfig();
//Rest same as previous code
}
}
static void bootstrapConfig () throws IOException {
ExecutionEnvironment executionEnvironment = ExecutionEnvironment.getExecutionEnvironment();
ExistingSavepoint existingSavepoint = Savepoint.load(executionEnvironment, "oldSavepointPath" , new MemoryStateBackend());
BootstrapTransformation<TestConfig> configTransformation = getConfigTransformation(executionEnvironment);
String newSavepointPath = "newSavepointPath" ;
existingSavepoint.withOperator( "classify_data" , configTransformation).write(newSavepointPath);
}
接下来,我们从旧目录加载保存点,然后更新操作员的状态。 要更新状态,我们需要在流作业中指定操作员的UID,并在步骤中创建转换。
完成后,我们可以在新路径中重写此修改的保存点。 请注意,新路径包含旧路径中指针的浅表副本。 这意味着删除旧的保存点路径将损坏新的保存点路径,因此您应避免这样做。
现在,您可以使用此新的保存点路径恢复Flink作业。
bin/flink run -s newSavepointPath test- checkpoint .jar
您甚至可以创建一个新的保存点,而不用更新旧的保存点。 为此,您需要执行Savepoint.create()而不是Savepoint.load()
Flink的状态处理器API是最受欢迎的功能之一,现在终于来了。 该API仅在1.9.0及更高版本中可用。
您可以在官方文档中探索整个API。
在 LinkedIn 或 Twitter上 与我联系, 或 发送电子邮件至 kharekartik@gmail.com
From: https://hackernoon.com/developers-the-api-to-bootstrap-your-flink-jobs-has-arrived-0h2c3zt9