Flume-NG中的hdfs sink的路径名(对应参数"hdfs.path",不允许为空)以及文件前缀(对应参数"hdfs.filePrefix")支持正则解析时间戳自动按时间创建目录及文件前缀。

  在实际使用中发现Flume内置的基于正则的解析方式非常耗时,有非常大的提升空间。如果你不需要配置按时间戳解析时间,那这篇文章对你用处不大,hdfs sink对应的解析时间戳的代码位于org.apache.flume.sink.hdfs.HDFSEventSink的process()方法中,涉及两句代码:  

 // reconstruct the path name by substituting place holders
String realPath = BucketPath.escapeString(filePath, event.getHeaders(),
timeZone, needRounding, roundUnit, roundValue, useLocalTime);
String realName = BucketPath.escapeString(fileName, event.getHeaders(),
timeZone, needRounding, roundUnit, roundValue, useLocalTime);

  其中,realPath是正则解析时间戳之后的完整路径名,filePath参数就是配置文件中的"hdfs.path";realName就是正则解析时间戳之后的文件名前缀,fileName参数就是配置文件中的"hdfs.filePrefix"。其他参数都相同,event.getHeaders()是一个Map里面有时间戳(可以通过interceptor、自定义、使用hdfs sink的useLocalTimeStamp参数三种方式来设置),其他参数是时区、是否四舍五入以及时间单位等。

  BucketPath.escapeString这个方法就是正则解析时间戳所在,具体代码我们不再分析,现在我们编写一个程序测试一下BucketPath.escapeString这个方法的性能,运行这个测试类要么在源码中:

public class Test {public static void main(String[] args) {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("timestamp", Long.toString(System.currentTimeMillis()));
String filePath = "hdfs://xxxx.com:8020/data/flume/%Y-%m-%d";
String fileName = "%H-%M";
long start = System.currentTimeMillis();
System.out.println("start time is:" + start);
for (int i = 0; i < 2400000; i++) {
        String realPath = BucketPath.escapeString(filePath, headers, null, false, Calendar.SECOND, 1, false);
        String realName = BucketPath.escapeString(fileName, headers, null, false, Calendar.SECOND, 1, false);
}
     long end = System.currentTimeMillis();
     System.out.println("end time is:"+ end + ".\nTotal time is:" + (end - start) + " ms.");
}
}

  这个方法后面5个参数我们一般不需要用到,因此这里其实都设置成在实际中没有影响的数值了。headers参数要有“timestamp”参数,我们这里循环处理240W个event,看看运行结果:

start time is:1412853253889
end time is:1412853278210.
Total time is:24321 ms.

  我靠,居然花了24s还多,尼玛要知道哥目前白天的数据量也就是每秒4W个event,这还不是峰值呢。。。加上解析时间戳全量就扛不住了,怎么办??

  能怎么办啊?只能想办法替换这个解析办法了,于是,我就想到这样了,看测试程序:

public class Test {

    private static SimpleDateFormat sdfYMD = null;
private static SimpleDateFormat sdfHM = null; public static void main(String[] args) { sdfYMD = new SimpleDateFormat("yyyy-MM-dd");
sdfHM = new SimpleDateFormat("HH-mm");
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("timestamp", Long.toString(System.currentTimeMillis()));
String filePath = "hdfs://dm056.tj.momo.com:8020/data/flume/%Y-%m-%d";
String fileName = "%H-%M";
long start = System.currentTimeMillis();
System.out.println("start time is:" + start);
for (int i = 0; i < 2400000; i++) {
//String realPath = BucketPath.escapeString(filePath, headers, null, false, Calendar.SECOND, 1, false);
//String realName = BucketPath.escapeString(fileName, headers, null, false, Calendar.SECOND, 1, false); String realPath = getTime("yyyy-MM-dd",Long.parseLong(headers.get("timestamp")));
String realName = getTime("HH-mm",Long.parseLong(headers.get("timestamp")));
}
long end = System.currentTimeMillis();
System.out.println("end time is:"+ end + ".\nTotal time is:" + (end - start) + " ms.");
} public static String getTime(String format,long timestamp) {
String time="";
if(format.equals("HH-mm"))
time=sdfHM.format(timestamp);
else if(format.equals("yyyy-MM-dd"))
time=sdfYMD.format(timestamp);
return time;
}
}

  我们使用java自己的SimpleDateFormat来完成按指定格式的解析,这样就不能将整个path或者name传进去了,看看运行结果:

start time is:1412853670246
end time is:1412853672204.
Total time is:1958 ms.

  尼玛!!!不是吧,不到2s。。。我这是在我的MBP上测试的,i5+8G+128G SSD,骚年你还犹豫什么呢?

  来开始改动源码吧。。。

  我们最好把解析格式做成可配置的,并且最好还保留原来的可以加前缀名的方式,因为有可能需要加入主机名啊什么的,但是可以把这个前缀作为中缀,解析时间戳的结果作为前缀。。。

  1、我们需要两个SimpleDateFormat来分别实现对path和name的格式化,并在配置时完成实例化,这样可以创建一次对象就Ok,还需要path和name的格式化串,这个可以做成全局的或者局部的,我们这是全局的(其实没有必要,是不是?哈哈),变量声明阶段代码:

   private SimpleDateFormat sdfPath = null;        //for file in hdfs path
private SimpleDateFormat sdfName = null; //for file name prefix private String filePathFormat;
private String fileNameFormat;

  2、configure(Context context)方法中需要对上述对象进行配置了,很简单,很明显,相关代码如下:

      filePath = Preconditions.checkNotNull(
context.getString("hdfs.path"), "hdfs.path is required");
filePathFormat = context.getString("hdfs.path.format", "yyyy/MM/dd"); //time's format ps:"yyyy-MM-dd"
sdfPath = new SimpleDateFormat(filePathFormat);
fileName = context.getString("hdfs.filePrefix", defaultFileName);
fileNameFormat = context.getString("hdfs.filePrefix.format", "HHmm");
sdfName = new SimpleDateFormat(fileNameFormat);

  增加的是上面的3、4、6、7四行代码,解析格式串是在"hdfs.path.format"和"hdfs.filePrefix.format"中进行配置,其它的地方不要存在时间戳格式串了,也不要出现原来内置的那些%H、%mm等等格式了。上面两个format配置有默认格式串,自己做决定就好。

  3、增加解析时间戳方法:

     public String getTime(String type,long timestamp) {
String time="";
if(type.equals("name"))
time=sdfName.format(timestamp);
else if(type.equals("path"))
time=sdfPath.format(timestamp);
return time;
}

  参数type用来指定是文件名的还是路径名的,用来调用相应地格式化对象。

  4、下面是重点了,上面几步即使配置了,不在这修改也不会起任何作用,修改process()方法,用以下代码替换最上面提到的两行代码:

                 String realPath = filePath;
String realName = fileName;
if(realName.equals("%host") && event.getHeaders().get("host") != null)
realName = event.getHeaders().get("host").toString();
if(event.getHeaders().get("timestamp") != null){
long time = Long.parseLong(event.getHeaders().get("timestamp"));
realPath += DIRECTORY_DELIMITER + getTime("path",time);
realName = getTime("name",time) + "." + realName;
}

  这几行的逻辑其实有:A、可以自定义中缀("hdfs.filePrefix",可以是常量或者是"%host",后者用来获取主机名,前提是要设置hostinterceptor);B、默认中缀就是默认的"FlumeData";C、如果headers中存在时间戳,调用getTime方法解析时间戳。

  5、编译&打包&替换&运行。。。

  哥打包比较原始,因为只修改了一个类,就把编译后的class文件以HDFSEventSink开头的几个class文件替换了原来flume-hdfs-sink的jar包中的对应的class文件。。。尼玛,原始吧。。。会maven,直接上maven吧。。。

  我这边的测试结果是如果没有配置压缩功能,性能提升超过70%,如果配置上压缩功能(gzip)性能提升超过50%,这数值仅供参考,不同环境不同主机不同人品可能不尽相同。。

  期待大伙的测试结果。。。

修改Flume-NG的hdfs sink解析时间戳源码大幅提高写入性能的更多相关文章

  1. Flume中的HDFS Sink配置参数说明【转】

    转:http://lxw1234.com/archives/2015/10/527.htm 关键字:flume.hdfs.sink.配置参数 Flume中的HDFS Sink应该是非常常用的,其中的配 ...

  2. Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

    摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就 ...

  3. 解析 ViewTreeObserver 源码(下)

    继上篇内容,本文介绍 ViewTreeObserver 的使用,以及体会其所涉及的观察者模式,期间会附带回顾一些基础知识.最后,我们简单聊一下 Android 的消息传递,附高清示意图,轻松捋清整个传 ...

  4. Jsoup解析网页源码时常用的Element(s)类

    Jsoup解析网页源码时常用的Element(s)类 一.简介 该类是Node的直接子类,同样实现了可克隆接口.类声明:public class Element extends Node 它表示由一个 ...

  5. HtmlAgilityPack --解析Html源码

    最近项目需要从网络上抓取一下数据解析Html源码,奈何正则表达式难写,于是网上搜索找到了“ HtmlAgilityPack”类库,敏捷开发,果然效率非同寻常. 在此做笔记,写下心得,顺便给自己总结一下 ...

  6. mvc5 解析route源码实现自己的route系统

    Asp.net mvc5 解析route源码实现自己的route系统   url route 路由系统的责任是找到匹配的路由,创建路由数据,并将请求分配给一个处理程序. 选择动作是 MVC 的处理程序 ...

  7. 浩哥解析MyBatis源码(十)——Type类型模块之类型处理器

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6715063.html 1.回顾 之前的两篇分别解析了类型别名注册器和类型处理器注册器,此二 ...

  8. 用Beautiful Soup解析html源码

    #xiaodeng #python3 #用Beautiful Soup解析html源码 html_doc = """ <html> <head> ...

  9. 二十三、并发编程之深入解析Condition源码

    二十三.并发编程之深入解析Condition源码   一.Condition简介 1.Object的wait和notify/notifyAll方法与Condition区别 任何一个java对象都继承于 ...

随机推荐

  1. 20155301-滕树晨 第二次随笔作业--从现有技能获取的经验应用于JAVA中

    第二次随笔--从现有技能获取的经验应用于JAVA中 你有什么技能比大多人(超过90%以上)更好? 这个想了半天,有一个是我乒乓球还是比较擅长的,在学校里可能比百分之90的人要强,在外面肯定是不如了.再 ...

  2. Linux c实现服务端与客户端聊天

    主要利用socket通信实现,具体代码如下 客户端: #include <stdio.h> #include <stdlib.h> #include <string.h& ...

  3. 怎样写 OpenStack Neutron 的 Plugin (一)

    鉴于不知道Neutron的人也不会看这篇文章,而知道的人也不用我再啰嗦Neutron是什么东西,我决定跳过Neutron简介,直接爆料. 首先要介绍一下我的开发环境.我没有使用DevStack,而是直 ...

  4. 要期末了搞不了OI了额……

    TAT~~~~~~期中完挂,求RP求期末逆袭

  5. 《TCP/IP详解卷1:协议》第11章 UDP:用户数据报协议-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  6. [AaronYang]C#人爱学不学[6]

    不要回头,不要将就,做到这两点,人生就会简单很多幸福很多 --Aaronyang的博客(www.ayjs.net)-www.8mi.me 1. 运算符,还有哪些你能学到? 1.1 不安全运算符: si ...

  7. 【web必知必会】—— 使用DOM完成属性填充

    本文介绍了使用DOM的简单方法实现动态加载图片的功能. 前文介绍了: 1 DOM四个常用的方法 首先看一下效果,初始时是一个相册,可以点击导航,切换图片,并切换下方显示内容: 点击house,可以动态 ...

  8. 前端自动化神器LiveReload配合浏览器和less/sass使用方法

    前言:搜了半天,各种推荐,什么十大工具啦.优秀工具集合啦之类的咸淡文章,就是没有一个讲怎么弄的.配合官网的article自己研究了半天总算配置好了.顺便吐槽下官网关于sass/less设置这块说的模糊 ...

  9. IntelliJ Idea13无法创建maven模板

    一.错误信息: -Dmaven.multiModuleProjectDirectory system property is not set. Check $M2_HOME environment v ...

  10. 【BZOJ 3036】 绿豆蛙的归宿

    求期望的题目(~~~water~~~) 压了下代码,压成15行hhh: 我把代码压成这么丑估计也没有人看吧: 毕竟是zky讲的一个水题,就当给博客除草了:    dfs回溯时求当前节点的f,除以当前节 ...