今天看到有小伙伴在问,就想着自己实现一下。

问题: Flink FileSink根据输入数据指定输出位置,比如讲对应日期的数据输出到对应目录

输入数据:
20190716 输出到路径 20190716
20190717 输出到路径 20190717
20190718 输出到路径 20190718

目前flink 对与输出到文件有两种实现(write 算子不算,只能指定目录):Rolling File Sink 和 Streaming File Sink

Rolling File Sink 的实现就是 BucketingSink,使用也很简单,直接指定路径就可以了,也可以设置如:目录名称格式(按时间格式滚动),输出文件格式,文件大小、滚动间隔、文件前缀、后缀一类的

// the SequenceFileWriter only works with Flink Tuples
import org.apache.flink.api.java.tuple.Tuple2
val input: DataStream[Tuple2[A, B]] = ... val sink = new BucketingSink[Tuple2[IntWritable, Text]]("/base/path")
sink.setBucketer(new DateTimeBucketer("yyyy-MM-dd--HHmm", ZoneId.of("America/Los_Angeles")))
sink.setWriter(new SequenceFileWriter[IntWritable, Text])
sink.setBatchSize(1024 * 1024 * 400) // this is 400 MB,
sink.setBatchRolloverInterval(20 * 60 * 1000); // this is 20 mins input.addSink(sink)

当然,如果是这么简单,就不会有这篇博客了,下面进入主题

--------------------------------------

默认的 DateTimeBucketer 只能根据时间指定文件名的滚动是规则,没办法根据数据指定文件的输出位置,这需要实现 BasePathBucketer 自定义输出路径

实现如下:

import java.io.File
import org.apache.flink.streaming.connectors.fs.Clock
import org.apache.flink.streaming.connectors.fs.bucketing.BasePathBucketer
import org.apache.hadoop.fs.Path /**
* 根据实际数据返回数据输出的路径
*/
class DayBasePathBucketer extends BasePathBucketer[String]{ /**
* 返回路径
* @param clock
* @param basePath
* @param element
* @return
*/
override def getBucketPath(clock: Clock, basePath: Path, element: String): Path = {
// yyyyMMdd
val day = element.substring(1, 9)
new Path(basePath + File.separator +
day)
}

}

调用如下:

import java.io.File
import java.text.SimpleDateFormat
import com.venn.index.conf.Common
import org.apache.flink.formats.json.JsonNodeDeserializationSchema
import org.apache.flink.runtime.state.filesystem.FsStateBackend
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.node.ObjectNode
import org.apache.flink.streaming.api.{CheckpointingMode, TimeCharacteristic}
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.streaming.connectors.fs.StringWriter
import org.apache.flink.streaming.connectors.fs.bucketing.BucketingSink
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer
import org.apache.flink.api.scala._ /**
* 使用BucketingSink 实现 根据‘数据’自定义输出目录
*/
object RollingFileSinkDemo { def main(args: Array[String]): Unit = { val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) val sdf = new SimpleDateFormat("yyyyMMddHHmmss")
val source = new FlinkKafkaConsumer[ObjectNode]("roll_file_sink", new JsonNodeDeserializationSchema, Common.getProp) val sink = new BucketingSink[String]("D:\\idea_out\\rollfilesink")
sink.setBucketer(new DayBasePathBucketer)
sink.setWriter(new StringWriter[String])
sink.setBatchSize(1024 * 1024 * 400) // this is 400 MB,
// sink.setBatchRolloverInterval(24 * 60 * 60 * 1000) // this is 24 hour
//    sink.setInProgressPrefix("inProcessPre")
// sink.setPendingPrefix("pendingpre")
// sink.setPartPrefix("partPre")

    env.addSource(source)
.assignAscendingTimestamps(json => {
sdf.parse(json.get("date").asText()).getTime
})
.map(json => {
json.get("date") + "-" + json.toString // 将日期拼接到前面,方便后面使用
})
.addSink(sink) env.execute("rollingFileSink")
} }

测试数据如下:

{"id" : 1, "name" : "venn1563288621091", "date" : "20190716230020"}
{"id" : 2, "name" : "venn1563288621310", "date" : "20190716231020"}
...
{"id" : 263, "name" : "venn1563288648926", "date" : "20190718184020"}
{"id" : 264, "name" : "venn1563288649029", "date" : "20190718185020"}
{"id" : 265, "name" : "venn1563288649132", "date" : "20190718190020"}

测试结果如下:

可以看到,当天的数据都输出到当天对应的目录中。

遇到个问题:

这里有个问题,因为重写了BasePathBucketer,自定义了输出文件,所有会同时打开多个输出文件,带来文件刷新的问题,在当前文件写完后(这里的表现是:当天的数据以及全部流过,
下一天的文件以及开始写了),会发现当天的文件中的数据不全,因为数据还没有全部刷到文件,这个时候下一个文件又开始写了,会发现上一个文件还没刷完

猜想:

猜想:每个文件都有个输出缓冲,上一个文件最后一点数据还在缓冲区,下一个文件又使用新的缓冲区,没办法刷到上一个文件的数据,只有等缓冲区数据满、超时一类的操作触发刷写 ??

验证:

源码BucketingSink.closePartFilesByTime 默认每60秒或大于滚动时间间隔(batchRolloverInterval)(系统时间) 将当前park文件,将状态从 in-process 修改为 pending,随后关闭当前的part 文件,数据刷到磁盘

代码如下:

private void closePartFilesByTime(long currentProcessingTime) throws Exception {

        synchronized (state.bucketStates) {
for (Map.Entry<String, BucketState<T>> entry : state.bucketStates.entrySet()) {
if ((entry.getValue().lastWrittenToTime < currentProcessingTime - inactiveBucketThreshold)
|| (entry.getValue().creationTime < currentProcessingTime - batchRolloverInterval)) {
LOG.debug("BucketingSink {} closing bucket due to inactivity of over {} ms.",
getRuntimeContext().getIndexOfThisSubtask(), inactiveBucketThreshold);
closeCurrentPartFile(entry.getValue());
}
}
}
}

下篇: Flink FileSink 自定义输出路径——StreamingFileSink、BucketingSink 和 StreamingFileSink简单比较

搞定

Flink FileSink 自定义输出路径——BucketingSink的更多相关文章

  1. Flink FileSink 自定义输出路径——StreamingFileSink、BucketingSink 和 StreamingFileSink简单比较

    接上篇:Flink FileSink 自定义输出路径——BucketingSink 上篇使用BucketingSink 实现了自定义输出路径,现在来看看 StreamingFileSink( 据说是S ...

  2. Hadoop案例(五)过滤日志及自定义日志输出路径(自定义OutputFormat)

    过滤日志及自定义日志输出路径(自定义OutputFormat) 1.需求分析 过滤输入的log日志中是否包含xyg (1)包含xyg的网站输出到e:/xyg.log (2)不包含xyg的网站输出到e: ...

  3. Lrc2srt精灵,增加自定义输出编码

    2015.4.8 对中文支持有点问题,修改了一下,支持自定义输出编码! 修改了建议行末偏移,通常100到200最好了,人的反应时间! http://files.cnblogs.com/files/ro ...

  4. VS 工程的 输出路径和工作路径的区别

    输出路径,是vs编译项目生成可执行文件的路径:工作路径是环境变量,比如我们在程序中写相对路径,就是以这个路径为基础的.在默认情况下,输出路径和工作路径都不写的话,默认是程序的bin下面的debug或者 ...

  5. HD1385Minimum Transport Cost(Floyd + 输出路径)

    Minimum Transport Cost Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/O ...

  6. EDIUS设置自定义输出的方法

    在做后期视频剪辑时,往往根据需求,需要输出不同分辨率格式的视频文件,那在EDIUS中,如何自定义输出设置,使之符合自己的需要呢?下面小编就来详细讲讲EDIUS自定义输出的一二事吧. 当剪辑完影片,设置 ...

  7. C++builder XE 安装控件 及输出路径

    C++builder XE 安装控件 与cb6不一样了,和delphi可以共用一个包. 启动RAD Studio.打开包文件. Project>Options>Delphi Compile ...

  8. HDU 1385 Minimum Transport Cost (最短路,并输出路径)

    题意:给你n个城市,一些城市之间会有一些道路,有边权.并且每个城市都会有一些费用. 然后你一些起点和终点,问你从起点到终点最少需要多少路途. 除了起点和终点,最短路的图中的每个城市的费用都要加上. 思 ...

  9. web项目Log4j日志输出路径配置问题

    问题描述:一个web项目想在一个tomcat下运行多个实例(通过修改war包名称的实现),然后每个实例都将日志输出到tomcat的logs目录下实例名命名的文件夹下进行区分查看每个实例日志,要求通过尽 ...

随机推荐

  1. c99的新功能

    在ANSI的标准确立后,C语言的规范在一段时间内没有大的变动,然而C++在自己的标准化创建过程中继续发展壮大.<标准修正案一>在1994年为C语言创建了一个新标准,但是只修正了一些C89标 ...

  2. RCNN,Fast RCNN,Faster RCNN 的前生今世:(4) Fast RCNN 算法详解

    继2014年的RCNN之后,Ross Girshick在15年推出Fast RCNN,构思精巧,流程更为紧凑,大幅提升了目标检测的速度.在Github上提供了源码. 同样使用最大规模的网络,Fast ...

  3. printf的使用

    #!/bin/bashprintf "|------------------------------------\n"printf "this is printf str ...

  4. flutter踩坑小记:The number of method references in a .dex file cannot exceed 64K.

    The number of method references in a .dex file cannot exceed 64K. 这句话的意思翻译出来是:.dex文件中的方法引用数不能超过64K. ...

  5. 第02组 Alpha冲刺(2/6)

    队名:無駄無駄 组长博客 作业博客 组员情况 张越洋 过去两天完成了哪些任务 任务分配.进度监督 提交记录(全组共用) 接下来的计划 沟通前后端成员,监督.提醒他们尽快完成各自的进度 还剩下哪些任务 ...

  6. V语言 基本使用

    新手必看-如何安装配置vlang运行环境(linux,macOS篇) 前置条件 发稿截止前只有Linux 或者 macOS系统能编译通过. 你需要安装clang或gcc 如果是macOS上需运行xco ...

  7. VMware 桥接网络设置

    1. 桥接的基本原理 桥接是将虚拟机和宿主机在局域网中的地位看成是一样的,逻辑如下: 但实际上是通过如下图实现的:(该图来自http://blog.csdn.net/qingfengtsing/art ...

  8. Git是怎么Ignore文件的?

    Git is one of the most popular version control systems (VCS) available, especially thanks to hosting ...

  9. Centos7 中查找文件、目录、内容

    1.查找文件 find / -name ‘filename’ 2.查找目录 find / -name ‘path’ -type d 3.查找内容 find . | xargs grep -ri ‘co ...

  10. 腾讯云CENTOS7安装MSSQL2017

    腾讯云CENTOS7安装MSSQL2017 mkdir -p /opt/sqlserver2017cd /opt/sqlserver2017/ 下载离线包:wget https://packages. ...