Flink Kafka Producer中的一次语义

时间:2019-08-01 12:01:49

标签: apache-kafka apache-flink

我正在尝试使用Kafka Source和Sink测试Flink一次精确的语义:

  1. 运行flink应用程序,只需将消息从一个主题转移到另一个主题,并行度为1,检查点间隔为20秒
  2. 每2秒使用Python脚本生成带有递增整数的消息。
  3. 与控制台使用者一起以read_committed隔离级别读取输出主题。
  4. 手动杀死TaskManager

我希望无论TaskManager是否被杀死和恢复,输出主题中的整数都会单调增加。

但是实际上在控制台用户输出中看到了一些意外情况:

32
33
34
35
36
37
38
39
40
-- TaskManagerKilled
32
34
35
36
40
41
46
31
33
37
38
39
42
43
44
45

看起来像检查点之间在输出主题中重播的所有消息。 它应该是正确的行为还是我做错了什么?

已还原一个快照: Flink UI

我的Flink代码:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
        env.getConfig().setAutoWatermarkInterval(1000);
        env.enableCheckpointing(20000, CheckpointingMode.EXACTLY_ONCE);
        env.setStateBackend(new RocksDBStateBackend("hdfs:///checkpoints-data"));

        Properties producerProperty = new Properties();
        producerProperty.setProperty("bootstrap.servers", ...);
        producerProperty.setProperty("zookeeper.connect", ...);
        producerProperty.setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG,"10000");
        producerProperty.setProperty(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"my-transaction");
        producerProperty.setProperty(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");

        Properties consumerProperty = new Properties();
        consumerProperty.setProperty("bootstrap.servers", ...);
        consumerProperty.setProperty("zookeeper.connect", ...);
        consumerProperty.setProperty("group.id", "test2");

        FlinkKafkaConsumer<String> consumer1 = new FlinkKafkaConsumer<String>("stringTopic1", new ComplexStringSchema(), consumerProperty);
        consumer1.assignTimestampsAndWatermarks(new PeriodicAssigner());

        FlinkKafkaProducer<String> producer1 = new FlinkKafkaProducer<String>("test",  new KeyedSerializationSchemaWrapper(new SimpleStringSchema()), producerProperty, FlinkKafkaProducer.Semantic.EXACTLY_ONCE);
        producer1.ignoreFailuresAfterTransactionTimeout();
        DataStreamSource<String> s1 = env.addSource(consumer1);
        s1.addSink(producer1);
        env.execute("Test");
    }

2 个答案:

答案 0 :(得分:1)

Flink定期生成可配置的检查点。恢复检查点后,Flink将状态回退到输入流中最后一个检查点的位置(不一定与上次处理/消耗的位置相同)。有多种方法可以确保一次语义。您可以使用支持一次语义的使用者(接收器),请参阅:Fault Tolerance Guarantees in Flink sinks

或者,您可以在使用者中支持一次语义。假设使用多个工作程序持久存储的唯一整数(并行度> 1),确保一次处理的一种方法如下:

  1. 假定当前检查点ID为CkptN。以Ckpt N的状态存储所有已处理的整数(在大事件的情况下为已处理事件的指纹)。您可以通过让使用者实现ListCheckpointed接口进行存储来实现此目的。 Cppt N中的状态(指纹或您的情况下的整数)。

  2. 一旦Flink移至下一个检查点(Ckpt N + 1),将存储在Ckpt N状态下的所有整数过滤掉,以确保进行一次精确的处理。将未过滤的已处理整数(或已处理事件的指纹)存储为Ckpt N +1的状态(即丢弃Ckpt N的状态)。

您只需要存储在两个检查点之间发生的已处理事件(或您的情况下的整数)的指纹,并在以后保留新的检查点时将其丢弃。

答案 1 :(得分:1)

除了将生产者设置为一次语义之外,还需要将消费者配置为仅从kafka中读取已提交的消息。默认情况下,使用者将读取已提交和未提交的消息。将此设置添加到您的消费者可以使您更接近所需的行为。

consumerProperties.setProperty(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed");