flink exactly-once系列目录:
一、两阶段提交概述 二、两阶段提交实现分析 三、StreamingFileSink分析 四、事务性输出实现 五、最终一致性实现
flink本身提供了到端的Exactly-Once的语义实现提供了两种连接器,一种是输出kafka, 上篇所分析的FlinkKafkaProducer011,另外一种是StreamingFileSink 文件输出,本节所要分析的内容。
一、StreamingFileSink使用
StreamingFileSink 是以分桶方式组织数据,可将相同类型的数据存放在一个桶里面,桶代表的是文件夹,文件夹下面可以有很多个文件,文件的生成方式可以是定量或者是定时,数据分桶可以通过BucketAssigner来指定,桶下面的文件生成策略使用RollingPolicy来指定,先看一个简单的例子:
通过StreamingFileSink.forRowFormat指定文件的跟目录与文件写入编码方式,这里使用SimpleStringEncoder 以UTF-8字符串编码方式写入文件,BucketAssigner指定分桶方式与序列化方式,getBucketId 方法解析数据获取所属桶ID,getSerializer指定序列化方式(带有版本信息,默认是1) ,withRollingPolicy 指定文件滚动策略,当文件大小超过1M或者10min滚动一次,withInactivityInterval表示文件最近一次更新时间至当前时间超过5min 同样滚动一次。
根目录下面以桶的方式组织,2019-10-15 19就代表一个桶,桶下面就是具体的数据文件称之为partFile,partFile文件命名方式part-subtaskIndex-count。StreamingFileSink 将partFile分为三种状态:in-process、in-pending、finshed,前两种表示中间状态文件,文件名称以点号开头,对于处理程序是不可读的,最后一种就是最终生成的文件,是可读的。
示例给出的方式文件是以普通的行存储方式存储的,还可以使用forBulkFormat使用块存储方式,但是需要用户自己指定写入方式,flink 本身提供了parquet的存储方式实现,只需要使用ParquetAvroWriters 即可。
二、StreamingFileSink Exactly-once
StreamingFileSink 与FlinkKafkaProducer011不同,它并不继承TwoPhaseCommitSinkFunction,但是同样使用两阶段提交方式,实现了CheckpointedFunction与CheckpointListener接口,
ProcessingTimeCallback用于定时检查回滚策略。其Exactly-once 的实现需要两阶段提交与partFile文件状态配合完成,
写入的文件有三种状态:in-process、in-pending、finshed,invoke方法里面正在写入的文件状态是in-process,当满足滚动策略之后将文件变为in-pending状态,执行sapshotState方法会对in-process状态文件执行commit操作,将缓存的数据刷进磁盘,并且记录其当前offset值,同时会记录in-pending文件的元数据信息,最终在notifyCheckpointComplete方法中将记录的in-pending状态文件转换为finshed的可读文件。如果中间程序出现异常则会通过initializeState完成恢复操作,将in-process文件恢复到记录的offset位置,直接恢复in-pending文件,并且将没有记录的in-pending文件删除、超过offset部分的数据删除,finshed文件无需操作。除了需要对offset、文件元数据信息保存外,还需要保存counter值,partFile命名组成的一部分,如果不保存则会造成重启后文件counter重新从0开始,会覆盖之前写入的文件。