Flink FileSink 自定义输出路径——BucketingSink
今天看到有小伙伴在问,就想着自己实现一下。
问题: 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的更多相关文章
- Flink FileSink 自定义输出路径——StreamingFileSink、BucketingSink 和 StreamingFileSink简单比较
接上篇:Flink FileSink 自定义输出路径——BucketingSink 上篇使用BucketingSink 实现了自定义输出路径,现在来看看 StreamingFileSink( 据说是S ...
- Hadoop案例(五)过滤日志及自定义日志输出路径(自定义OutputFormat)
过滤日志及自定义日志输出路径(自定义OutputFormat) 1.需求分析 过滤输入的log日志中是否包含xyg (1)包含xyg的网站输出到e:/xyg.log (2)不包含xyg的网站输出到e: ...
- Lrc2srt精灵,增加自定义输出编码
2015.4.8 对中文支持有点问题,修改了一下,支持自定义输出编码! 修改了建议行末偏移,通常100到200最好了,人的反应时间! http://files.cnblogs.com/files/ro ...
- VS 工程的 输出路径和工作路径的区别
输出路径,是vs编译项目生成可执行文件的路径:工作路径是环境变量,比如我们在程序中写相对路径,就是以这个路径为基础的.在默认情况下,输出路径和工作路径都不写的话,默认是程序的bin下面的debug或者 ...
- HD1385Minimum Transport Cost(Floyd + 输出路径)
Minimum Transport Cost Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/O ...
- EDIUS设置自定义输出的方法
在做后期视频剪辑时,往往根据需求,需要输出不同分辨率格式的视频文件,那在EDIUS中,如何自定义输出设置,使之符合自己的需要呢?下面小编就来详细讲讲EDIUS自定义输出的一二事吧. 当剪辑完影片,设置 ...
- C++builder XE 安装控件 及输出路径
C++builder XE 安装控件 与cb6不一样了,和delphi可以共用一个包. 启动RAD Studio.打开包文件. Project>Options>Delphi Compile ...
- HDU 1385 Minimum Transport Cost (最短路,并输出路径)
题意:给你n个城市,一些城市之间会有一些道路,有边权.并且每个城市都会有一些费用. 然后你一些起点和终点,问你从起点到终点最少需要多少路途. 除了起点和终点,最短路的图中的每个城市的费用都要加上. 思 ...
- web项目Log4j日志输出路径配置问题
问题描述:一个web项目想在一个tomcat下运行多个实例(通过修改war包名称的实现),然后每个实例都将日志输出到tomcat的logs目录下实例名命名的文件夹下进行区分查看每个实例日志,要求通过尽 ...
随机推荐
- php实现大文件上传分片上传断点续传
前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...
- COM Error Code(HRESULT)部分摘录
Return value/code Description 0x00030200 STG_S_CONVERTED The underlying file was converted to compou ...
- C# 跨域 请求带cookie
原文:https://blog.csdn.net/z69183787/article/details/78954325 背景: 别个的项目,要开发App接口,要求用前端AJAX的方式访问接口数据. 后 ...
- imagick的简单使用
原文:https://blog.csdn.net/wulove52/article/details/78376142 PHP建图通常都用GD库,因为是内置的不需要在服务器上额外安装插件,所以用起来比较 ...
- leetcode解题报告(25):Reverse Words in a String III
描述 Given a string, you need to reverse the order of characters in each word within a sentence while ...
- poj 3735 稀疏矩阵矩阵快速幂
设人数为 $n$,构造 $(n + 1) \times (n + 1)$ 的矩阵 得花生:将改行的最后一列元素 $+ 1$ \begin{gather}\begin{bmatrix}1 & 0 ...
- ZwQuerySystemInfoMation函数使用
ZwQueryInfoMation函数很简单.就是4个参数. NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLA ...
- Codeforces 1239E. Turtle 折半
原文链接www.cnblogs.com/zhouzhendong/p/CF1239E.html 前言 咕了这么久之后,我的博客复活了! 题解 结论1 存在一个最优解\(A\)数组,满足\(\foral ...
- mac eclipse 创建Java 工程
首先创建Java工程testjavapro,创建包testjavapro,接着创建类testjava 参考: https://www.jianshu.com/p/20280b850c95
- Leetcode Majority Element系列 摩尔投票法
先看一题,洛谷2397: 题目背景 自动上次redbag用加法好好的刁难过了yyy同学以后,yyy十分愤怒.他还击给了redbag一题,但是这题他惊讶的发现自己居然也不会,所以只好找你 题目描述 [h ...