flink 端到端的一致性 实现

25 篇文章 0 订阅
16 篇文章 4 订阅

一、综述

flink 通过内部依赖checkpoint 并且可以通过设置其参数exactly-once 实现其内部的一致性。但要实现其端到端的一致性,还必须保证
1、source 外部数据源可重设数据的读取位置
2、sink端 需要保证数据从故障恢复时,数据不会重复写入外部系统(或者可以逻辑实现写入多次,但只有一次生效的数据sink端

二、sink 端到端实现方式

幂等操作:
一个操作,可以重复执行多次,但只导致一次结果更改,豁免重复操作执行就不起作用了,他的瑕疵 (在系统恢复的过程中,如果这段时间内多个更新或者插入导致状态不一致,但当数据追上就可以了)
(逻辑与、逻辑或等)具体理解参照自己以前写的文章。
事务写入:
事务应该具有四个属性:原子性、一致性、隔离性、持久性等。其具体的实现方式有两种
(1)、预写日志
简单易于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink系统,都能用这种方式一批搞定,DataStream API提供了一个模板类:GenericWriteAheadSink,来实现这种事务性sink;
缺点:
1)、sink系统没说他支持事务。有可能出现一部分写入集群了。一部分没有写进去(如果实表,再写一次就写重复了)
2)、checkpoint做完了sink才去真正的写入(但其实得等sink都写完checkpoint才能生效,所以WAL这个机制jobmanager确定它写完还不算真正写完,还得有一个外部系统已经确认 完成的checkpoint)
2)、两阶段提交。 flink 真正实现exactle-once
对于每个checkpoint,sink 任务会启动一个事务,并将接下来所有接收的数据添加到事务中,然后将这些数据写入外部sink系统,但不提交他们(这里是预提交)。当checkpoint完成时的通知,它才正式提交事务,实现结果的真正写入;这种方式真正实现了exactly-once,它需要一个提供事务支持的外部sink系统,Flink提供了其具体实现(TwoPhaseCommitSinkFunction接口)

三、 2pc 对外部 sink的要求

1、外部sink系统必须事务支持,或者sink任务必须能够模拟外部系统上的事务;
2、在checkpoint的间隔期间里,必须能够开启一个事务,并接受数据写入。
3、在收到checkpoint完成通知之前,事务必须是“等待提交”的状态,在故障恢复的情况线,这可能需要一些时间。如果个时候sink系统关闭事务(例如超时了),那么未提交的数据就会丢失;
4、四年任务必选能够在进程失败后恢复事务
5、提交事务必须是幂等操作;

四、综上不同Source和sink的一致性保证:
在这里插入图片描述

五、应用(flinK+kafka 端到端一致性保证)

flink 和kafka 端到端一致性(kafka(source+flink+kafka(sink)))
1、内部 – 利用checkpoint机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
2、source – kafka consumer作为source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重置偏移量,重新消费数据,保证一致性;

kafka 0.8 和kafka 0.11 之后 通过以下配置将偏移量保存,恢复时候重新消费
 kafka.setStartFromLatest();
 kafka.setCommitOffsetsOnCheckpoints(false);
 kafka 0.9 和kafka0.10 未验证是否支持这两个参数(todo)

3、sink FlinkkafkaProducer作为Sink,采用两阶段提交的sink,由下图可以看出flink 0.11 已经默认继承了TwoPhaseCommitSinkFunction
在这里插入图片描述
但我们需要在参数种传入指定语义,它默认时还是at-least-once
此外我们还需要进行一些producer的容错配置:
(1)除了启用Flink的检查点之外,还可以通过将适当的semantic参数传递给FlinkKafkaProducer011(FlinkKafkaProducer对于Kafka> = 1.0.0版本)
(2)来选择三种不同的操作模式
1)、Semantic.NONE 代表at-mostly-once语义
2)、Semantic.AT_LEAST_ONCE(Flink默认设置
3)、Semantic.EXACTLY_ONCE 使用Kafka事务提供一次精确的语义,每当您使用事务写入Kafka时
(3)、请不要忘记消费kafka记录任何应用程序设置所需的设置isolation.leva(read_committed 或者read_uncommitted-后者是默认)
read_committed,只是读取已经提交的数据。

应用;
Semantic.EXACTLY_ONCE依赖与下游系统能支持事务操作.以0.11kafka为例.
transaction.max.timeout.ms 最大超市时长,默认15分钟,如果需要用exactly语义,需要增加这个值。(因为它小于transaction.timeout.ms )
isolation.level 如果需要用到exactly语义,需要在下级consumerConfig中设置read-commited [read-uncommited(默认值)]
transaction.timeout.ms 默认为1hour

其参数对应关系为 和一些报错问题
checkpoint间隔<transaction.timeout.ms<transaction.max.timeout.ms

参考:https://www.cnblogs.com/createweb/p/11971846.html

注意:
1、Semantic.EXACTLY_ONCE 模式每个FlinkKafkaProducer011实例使用一个固定大小的KafkaProducers池。每个检查点使用这些生产者中的每一个。如果并发检查点的数量超过池大小,FlinkKafkaProducer011 将引发异常,并使整个应用程序失败。请相应地配置最大池大小和最大并发检查点数。

2、Semantic.EXACTLY_ONCE采取所有可能的措施,不要留下任何挥之不去的数据,否则这将有碍于消费者更多地阅读Kafka主题。但是,如果flink应用程序在第一个检查点之前失败,则在重新启动此类应用程序后,系统种将没有有关先前池大小信息,因此,在第一个检查点完成前按比例缩小Flink应用程序的FlinkKafkaProducer011.SAFE_SCALE_DOWN_FACTOR

//1。设置最大允许的并行checkpoint数,防止超过producer池的个数发生异常
env.getCheckpointConfig.setMaxConcurrentCheckpoints(5) 
//2。设置producer的ack传输配置
// 设置超市时长,默认15分钟,建议1个小时以上
producerConfig.put(ProducerConfig.ACKS_CONFIG, 1) 
producerConfig.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 15000) 

//3。在下一个kafka consumer的配置文件,或者代码中设置ISOLATION_LEVEL_CONFIG-read-commited
//Note:必须在下一个consumer中指定,当前指定是没用用的
kafkaonfigs.setProperty(ConsumerConfig.ISOLATION_LEVEL_CONFIG,"read_commited")

完整应用代码:

package com.shufang.flink.connectors

import java.util.Properties
import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer.Semantic
import org.apache.flink.streaming.connectors.kafka._
import org.apache.flink.streaming.util.serialization.KeyedSerializationSchemaWrapper
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.common.serialization.StringDeserializer

object KafkaSource01 {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    //这是checkpoint的超时时间
    //env.getCheckpointConfig.setCheckpointTimeout()
    //设置最大并行的chekpoint
    env.getCheckpointConfig.setMaxConcurrentCheckpoints(5)
    env.getCheckpointConfig.setCheckpointInterval(1000) //增加checkpoint的中间时长,保证可靠性


    /**
     * 为了保证数据的一致性,我们开启Flink的checkpoint一致性检查点机制,保证容错
     */
    env.enableCheckpointing(60000)

    /**
     * 从kafka获取数据,一定要记得添加checkpoint,能保证offset的状态可以重置,从数据源保证数据的一致性
     * 保证kafka代理的offset与checkpoint备份中保持状态一致
     */

    val kafkaonfigs = new Properties()

    //指定kafka的启动集群
    kafkaonfigs.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092")
    //指定消费者组
    kafkaonfigs.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "flinkConsumer")
    //指定key的反序列化类型
    kafkaonfigs.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
    //指定value的反序列化类型
    kafkaonfigs.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
    //指定自动消费offset的起点配置
    //    kafkaonfigs.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")


    /**
     * 自定义kafkaConsumer,同时可以指定从哪里开始消费
     * 开启了Flink的检查点之后,我们还要开启kafka-offset的检查点,通过kafkaConsumer.setCommitOffsetsOnCheckpoints(true)开启,
     * 一旦这个检查点开启,那么之前配置的 auto-commit-enable = true的配置就会自动失效
     */
    val kafkaConsumer = new FlinkKafkaConsumer[String](
      "console-topic",
      new SimpleStringSchema(), // 这个schema是将kafka的数据应设成Flink中的String类型
      kafkaonfigs
    )

    // 开启kafka-offset检查点状态保存机制
    kafkaConsumer.setCommitOffsetsOnCheckpoints(true)

    //    kafkaConsumer.setStartFromEarliest()//
    //    kafkaConsumer.setStartFromTimestamp(1010003794)
    //    kafkaConsumer.setStartFromLatest()
    //    kafkaConsumer.setStartFromSpecificOffsets(Map[KafkaTopicPartition,Long]()

    // 添加source数据源
    val kafkaStream: DataStream[String] = env.addSource(kafkaConsumer)

    kafkaStream.print()

    val sinkStream: DataStream[String] = kafkaStream.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(5)) {
      override def extractTimestamp(element: String): Long = {
        element.split(",")(1).toLong
      }
    })


    /**
     * 通过FlinkkafkaProduccer API将stream的数据写入到kafka的'sink-topic'中
     */
    //    val brokerList = "localhost:9092"
    val topic = "sink-topic"
    val producerConfig = new Properties()
    producerConfig.put(ProducerConfig.ACKS_CONFIG, new Integer(1)) // 设置producer的ack传输配置
    producerConfig.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, Time.hours(2)) //设置超市时长,默认1小时,建议1个小时以上

    /**
     * 自定义producer,可以通过不同的构造器创建
     */
    val producer: FlinkKafkaProducer[String] = new FlinkKafkaProducer[String](
      topic,
      new KeyedSerializationSchemaWrapper[String](SimpleStringSchema),
      producerConfig,
      Semantic.EXACTLY_ONCE
    )

    //    FlinkKafkaProducer.SAFE_SCALE_DOWN_FACTOR
    /** *****************************************************************************************************************
     * * 出了要开启flink的checkpoint功能,同时还要设置相关配置功能。
     * * 因在0.9或者0.10,默认的FlinkKafkaProducer只能保证at-least-once语义,假如需要满足at-least-once语义,我们还需要设置
     * * setLogFailuresOnly(boolean)    默认false
     * * setFlushOnCheckpoint(boolean)  默认true
     * * come from 官网 below:
     * * Besides enabling Flink’s checkpointing,you should also configure the setter methods setLogFailuresOnly(boolean)
     * * and setFlushOnCheckpoint(boolean) appropriately.
     * ******************************************************************************************************************/

    producer.setLogFailuresOnly(false) //默认是false


    /**
     * 除了启用Flink的检查点之外,还可以通过将适当的semantic参数传递给FlinkKafkaProducer011(FlinkKafkaProducer对于Kafka> = 1.0.0版本)
     * 来选择三种不同的操作模式:
     * Semantic.NONE  代表at-mostly-once语义
     * Semantic.AT_LEAST_ONCE(Flink默认设置)
     * Semantic.EXACTLY_ONCE:使用Kafka事务提供一次精确的语义,每当您使用事务写入Kafka时,
     * 请不要忘记为使用Kafka记录的任何应用程序设置所需的设置isolation.level(read_committed 或read_uncommitted-后者是默认值)
     */

    sinkStream.addSink(producer)

    env.execute("kafka source & sink")
  }
}

部分参考:https://blog.csdn.net/shufangreal/article/details/104737652?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值