flink实时写入hdfs之BucketingSink
背景
- flink写入hdfs有比spark的先天优势,就是自带api(可以配置文件滚动策略的方式,没有小文件的烦恼),而spark自带api有小文件问题,解决小文件问题还得自己用hadoop api去实现(其实也还好,就是不够优雅,实现来说相对也比较麻烦)
依赖
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-filesystem_2.11</artifactId>
<version>1.9.0</version>
</dependency>
代码
DataStream<String> input = ...;
BucketingSink<Row> sink = new BucketingSink<>("hdfs://data/test/");
//指定在/data/test/下的路径格式 '/data/test/2020-01-01/00'
sink.setBucketer(new DateTimeBucketer<>("yyyy-MM-dd/HH", ZoneId.of("Asia/Shanghai")));
//设置滚动大小 bytes,默认1024L * 1024L * 384L
sink.setBatchSize(1024L * 1024L * 384L);
//设置滚动间隔 ms, 默认Long.MAX_VALUE
sink.setBatchRolloverInterval(60000);
//还有空闲检测等参数,可以自行配置
input.addSink(hdfsSink)
问题:怎么根据数据上的时间来区分写入目录
上面的代码只是根据写入的时间来区分目录,如何根据数据上的时间来分别写入不同目录?
例如000, 1577844000000
这条数据不一定会写入到哪个目录,但是我想根据1577844000000
来写入到相应时间的/data/test/2020-01-01/00
下,这样即使数据乱序,也可以分别写入到它对应的时间的目录
解决
- 我们可以实现自己的Bucketer
public class MyDateTimeBucketer implements Bucketer<Row> {
//dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd/HH").withZone(zoneId)
//省略部分代码...
/**
* 这个方法用来指定数据写入到哪个目录
*/
@Override
public Path getBucketPath(Clock clock, Path basePath, Row element) {
String fieldTime = element.getField(1).toString();
if (fieldTime.length() == 10) {
fieldTime = fieldTime + "000";
}
String newDateTimeString = dateTimeFormatter.format(Instant.ofEpochMilli(Long.parseLong(fieldTime)));
return new Path(basePath + "/" + newDateTimeString);
}
这样,数据就可以写入到正确的分区,hive也可以建立相应的分区表去读了
有说的不对的地方,或者想交流的 随时砸我