https://cloud.tencent.com/developer/article/1025430

Flume-Hbase-Sink针对不同版本flume与HBase的适配研究与经验总结

导语:本文细致而全面地讲解使用flume输出数据到HBase的三种不同 Flume-Hbase-Sink 之间的差异性,以及技术细节。并且透彻而全面地总结了不同版本flume和HBase之间的兼容性问题。 为了更加详细说明三种不同hbasesink的差异性,本文在附录附上详细的源码解读。

一、Flume 的HBaseSinks 详细介绍

Flume 有两大类 HBasesinks: HBaseSink (org.apache.flume.sink.hbase.HBaseSink) 和 AsyncHBaseSink (org.apache.flume.sink.hbase.AsyncHBaseSink) 。

1.1、HBasesink

提供两种序列化模式:

1.1.1、SimpleHbaseEventSerializer

将整个事件event的body部分当做完整的一列写入hbase,因此在插入HBase的时候,一个event的body只能被插入一个column;

1.1.2、RegexHbaseEventSerializer

根据正则表达式将event 的body拆分到不同的列当中,因此在插入HBase的时候,支持用户自定义插入同一个rowkey对应的同一个columnFamily 的多个column。

【优点】

(a) 安全性较高:支持secure HBase clusters (FLUME-1626) ,支持往secure hbase写数据(hbase可以开启kerberos校验);

(b) 支持0.96及以上版本的HBase 的IPC通信----- the new HBase IPC which was introduced in HBase 0.96 and up。

【缺点】

性能没有AsyncHBaseSink高。因为HBaseSink采用阻塞调用(blocking calls),而AsyncHBaseSink采用非阻塞调用(non-blocking calls)。

1.2、AsyncHBaseSink

目前只提供一种序列化模式:SimpleAsyncHbaseEventSerializer:

将整个事件event的body部分当做完整的一列写入hbase,因此在插入HBase的时候,一个event的body只能被插入一个column。

【优点】

AsyncHBaseSink采用非阻塞调用(non-blocking calls),因此,性能比HBaseSink高;

【缺点】

(a) 不支持secure HBase clusters (FLUME-1626),不支持往secure hbase写数据;

(b) 不支持0.96及以上版本的HBase 的IPC通信----- the new HBase IPC which was introduced in HBase 0.96 and up。

二、两大类HBasesinks的详细用法

2.1 HBasesink--SimpleHbaseEventSerializer

Required properties 如下表格所示:

Property Name

Default

Description

channel

 

type

The component type name, needs to be org.apache.flume.sink.HBaseSink

table

The name of the table in Hbase to write to.

columnFamily

The column family in Hbase to write to.

batchSize

100

Number of events to be written per txn.

serializer

org.apache.flume.sink.hbase.SimpleHbaseEventSerializer;org.apache.flume.sink.hbase.RegexHbaseEventSerializer

two serializers are provided with flume inHBasesink

serializer.*

Properties to be passed to the serializer

如下是展示如何使用 HBasesink--SimpleHbaseEventSerializer:

agenttest.channels = memoryChannel-1
agenttest.sinks = hbaseSink-1
agenttest.sinks.hbaseSink-1.type = org.apache.flume.sink.hbase.HBaseSink
agenttest.sinks.hbaseSink-1.table = test_hbase_table //HBase表名
agenttest.sinks.hbaseSink-1.columnFamily = familycolumn-1 //HBase表的列族名称
agenttest.sinks.hbaseSink-1.serializer= org.apache.flume.sink.hbase.SimpleHbaseEventSerializer
agenttest.sinks.hbaseSink-1.serializer.payloadColumn = columnname //HBase表的列族下的某个列名称
agenttest.sinks.hbaseSink-1.channels = memoryChannel-1

注:当指定存入到HBase表的某个列族的指定列column时,不能写成:

agenttest.sinks.hbaseSink-1.columnName = columnname
或者:
agenttest.sinks.hbaseSink-1.column = columnname

这些都是网上的错误写法!另外两个序列化模式也是不能这样使用。

2.2 HBasesink--RegexHbaseEventSerializer

如下是展示如何使用 HBasesink--RegexHbaseEventSerializer(使用正则匹配切割event,然后存入HBase表的多个列):

agenttest.channels = memoryChannel-2
agenttest.sinks = hbaseSink-2
agenttest.sinks.hbaseSink-2.type = org.apache.flume.sink.hbase.HBaseSink
agenttest.sinks.hbaseSink-2.table = test_hbase_table
agenttest.sinks.hbaseSink-2.columnFamily = familycolumn-2
agenttest.sinks.hbaseSink-2.serializer= org.apache.flume.sink.hbase.RegexHbaseEventSerializer
// 比如我要对nginx日志做分割,然后按列存储HBase,正则匹配分成的列为: ([xxx] [yyy] [zzz] [nnn] ...) 这种格式, 所以用下面的正则:
agent.sinks.hbaseSink-2.serializer.regex = \\[(.*?)\\]\\ \\[(.*?)\\]\\ \\[(.*?)\\]\\ \\[(.*?)\\]
// 指定上面正则匹配到的数据对应的hbase的familycolumn-2 列族下的4个cloumn列名
agent.sinks.hbaseSink-2.serializer.colNames = column-1,column-2,column-3,column-4
#agent.sinks.hbaseSink-2.serializer.payloadColumn = test
agenttest.sinks.hbaseSink-2.channels = memoryChannel-

2.3 AsyncHBaseSink--SimpleAsyncHbaseEventSerializer

Required properties 如下表格所示:

Property Name

Default

Description

channel

 

type

The component type name, needs to be org.apache.flume.sink.AsyncHBaseSink

table

The name of the table in Hbase to write to.

columnFamily

The column family in Hbase to write to.

batchSize

100

Number of events to be written per txn.

timeout

The length of time (in milliseconds) the sink waits for acks from hbase for all events in a transaction. If no timeout is specified, the sink will wait forever.

serializer

org.apache.flume.sink.hbase.SimpleAsyncHbaseEventSerializer

 

serializer.*

Properties to be passed to the serializer.

如下是展示如何使用 AsyncHBaseSink--SimpleAsyncHbaseEventSerializer:

agenttest.channels = memoryChannel-3
agenttest.sinks = hbaseSink-3
agenttest.sinks.hbaseSink-3.type = org.apache.flume.sink.hbase.AsyncHBaseSink
agenttest.sinks.hbaseSink-3.table = test_hbase_table
agenttest.sinks.hbaseSink-3.columnFamily = familycolumn-3
agenttest.sinks.hbaseSink-3.serializer = org.apache.flume.sink.hbase.SimpleAsyncHbaseEventSerializer
agenttest.sinks.hbaseSink-3.serializer.payloadColumn = columnname //HBase表的列族下的某个列名称
agenttest.sinks.hbaseSink-3.channels = memoryChannel-3

如果读者感兴趣,可以仔细阅读Apache flume官网,上面有一些更加详细的信息:

http://archive.cloudera.com/cdh/3/flume-ng/FlumeUserGuide.html

三、使用flume-hbase-sink的常见错误总结

3.1、无HBase读写权限

如果提交./flume-ng 任务的用户没有HBase的读写权限,那么就会出现无权限读写HBase:

[ERROR - org.apache.flume.lifecycle.LifecycleSupervisor$MonitorRunnable.run(LifecycleSupervisor.java:253)]Unable to start SinkRunner: { policy:org.apache.flume.sink.DefaultSinkProcessor@f46fdc1 counterGroup:{ name:null counters:{} } } - Exception follows.
org.apache.flume.FlumeException: Could not start sink. Table or column family does not exist in Hbase (Permission denied).

【解决方法】

需要为用户赋予HBase读写权限,或者超级管理员权限。

3.2、低版本flume使用错误的序列化模式,导致与HBase版本接口不匹配

本文作者使用 flume-1.6.0 的RegexHbaseEventSerializer(属于 HBasesink)对HBase-1.1.3 和 HBase-1.2.1进行插入数据,出现以下错误:

2016-12-22 12:14:50 ERROR [SinkRunner-PollingRunner-DefaultSinkProcessor] (org.apache.flume.sink.hbase.HBaseSink.process:351)  - Failed to commit transaction.Transaction rolled back.
java.lang.NoSuchMethodError: org.apache.hadoop.hbase.client.Put.setWriteToWAL(Z)V
at org.apache.flume.sink.hbase.HBaseSink$3.run(HBaseSink.java:377)
at org.apache.flume.sink.hbase.HBaseSink$3.run(HBaseSink.java:372)
at org.apache.flume.auth.SimpleAuthenticator.execute(SimpleAuthenticator.java:50)
at org.apache.flume.sink.hbase.HBaseSink.putEventsAndCommit(HBaseSink.java:372)
at org.apache.flume.sink.hbase.HBaseSink.process(HBaseSink.java:342)
at org.apache.flume.sink.DefaultSinkProcessor.process(DefaultSinkProcessor.java:68)
at org.apache.flume.SinkRunner$PollingRunner.run(SinkRunner.java:147)
at java.lang.Thread.run(Thread.java:745)

错误信息提示:

java.lang.NoSuchMethodError: org.apache.hadoop.hbase.client.Put.setWriteToWAL(Z)V

查看源码,SimpleHbaseEventSerializer和 RegexHbaseEventSerializer的getActions函数产生的是put对象实例,也就是org.apache.hadoop.hbase.client.Put实例(想要了解更详细的内容,可以阅读本文的【附录:源码解读】章节)。org.apache.hadoop.hbase.client.Put里的确包含setWriteToWAL(boolean write)这个函数。新版本的hbase(0.98以上版本),setWriteToWAL(boolean write)方法改变了返回值,从void 变成了 Mutation。而flume-1.6.0以及以下版本,无法适配setWriteToWAL(boolean write)的改变,从而导致出错。

与SimpleHbaseEventSerializer和 RegexHbaseEventSerializer不同的是,SimpleAsyncHbaseEventSerializer的getActions函数不是产生put实例,而是生成PutRequest实例。而PutRequest实例是可以与任意版本的HBase接口适配的。

想要了解更详细的内容,可以阅读本文的【附录:源码解读】章节。

【解决方法】

(1) 如果不改变flume的版本,那么需要将HBase降级到0.98 及以下版本;

(2) 如果不改变HBase版本,需要将flume升级到 1.7.0 及以上版本。

四、总结flume与HBase版本适配问题&&用户自定义HBase 的column

总结:经过上述解读,以及作者本人验证,有以下几条经验总结:

4.1 flume与HBase版本适配问题

4.1.1 对于HBasesink

(a) 对于Flume-1.6.0 及以下版本:HBasesink目前只支持往 HBase-0.98 版本及以下版本写入数据,当HBase超过0.98版本,1.6.0 及以下版本的Flume则不支持往HBase写入数据;

(b) 对于Flume-1.7.0 及以上版本:HBasesink目前支持往0.98及以上版本的HBase写入数据(当然也支持往0.98及以下版本的HBase写入数据);

4.1.2 对于AsyncHBaseSink

(a) 支持所有版本的HBase写入数据。

(b) 不支持0.96及以上版本的HBase 的IPC通信方式------ the new HBase IPC which was introduced in HBase 0.96 and up。

4.2 flume-hbase-sink支持用户自定义HBase的column

4.2.1 对于HBasesink

(a)序列化模式SimpleHbaseEventSerializer

将整个事件event的body部分当做完整的一列写入hbase,因此在插入HBase的时候,一个event的body只能被插入一个column;

(b) 序列化模式RegexHbaseEventSerializer

根据正则表达式将event body拆分到不同的列当中,因此在插入HBase的时候,支持用户自定义插入同一个rowkey对应的同一个columnFamily 的多个column。

4.2.2 对于AsyncHBaseSink

目前只提供一种序列化模式:SimpleAsyncHbaseEventSerializer:

将整个事件event的body部分当做完整的一列写入hbase,因此在插入HBase的时候,一个event的body只能被插入一个column。

【附录:源码解读】

1、HBasesink---SimpleHbaseEventSerializer

SimpleHbaseEventSerializer类中包括的函数有:

l  SimpleHbaseEventSerialzer函数:这是构造函数

l  configure(ComponentConfiguration conf)函数:这是配置函数,目前是空白。

l  close()函数:这是个关闭函数,估计是用于关闭调用资源的。

l  configure(Context context)函数:配置函数,主要作用是从flume的配置文件中读取信息。

l  initalize(Event event, byte[] cf): 从event中读取内容,并配置HBase的column family

l  getActions():创建put对象,每个put对应着hbase的一行数据。

l  getIncrements():将hbase的自增列加1

Configure(Context context)函数

 // 读取flume配置文件中的rowPrefix,rowPrefix的默认值是default
· rowPrefix = context.getString("rowPrefix", "default");
· // 读取flume配置文件中的incrementRow,默认值是inRow
· incrementRow =
· context.getString("incrementRow", "incRow").getBytes(Charsets.UTF_8);
· // 读取flume配置文件中的suffix,默认值是uuid
· String suffix = context.getString("suffix", "uuid");
· // 读取flume配置文件的payloadColumn,默认之是pCol。payloadColumn对应这hbase的列名
· String payloadColumn = context.getString("payloadColumn","pCol");
· // 读取flume配置文件中的incrementColumn,默认值是iCol
· String incColumn = context.getString("incrementColumn","iCol");
· if(payloadColumn != null && !payloadColumn.isEmpty()) {
· // 这几行代码是配置hbase中的rowkey前缀
· if(suffix.equals("timestamp")){
· keyType = KeyType.TS;
· } else if (suffix.equals("random")) {
· keyType = KeyType.RANDOM;
· } else if(suffix.equals("nano")){
· keyType = KeyType.TSNANO;
· } else {
· keyType = KeyType.UUID;
· }
· plCol = payloadColumn.getBytes(Charsets.UTF_8);
· }
· if(incColumn != null && !incColumn.isEmpty()) {
· incCol = incColumn.getBytes(Charsets.UTF_8);
· }

对于Configure函数,主要需要说明的flume配置文件和代码之间的对应关系。

比如,如果你在flume的配置文件中有一行如:a1.sinks.k1.serializer.payloadColumn=colName。 那么Configure函数中的context.getString("payloadColumn", "pCol")的返回值就是colName.

同样如果你设置 a1.sinks.k1.serializer.rowPrefix=123456, 那么context.getString("rowPrefix", "default")的返回值就是123456.

initalize(Event event, byte[] cf)函数

·         public void initialize(Event event, byte[] cf) {
· this.payload = event.getBody();
· this.cf = cf;
· }

这个函数代码简单,cf表示hbase中的column family; event是flume的一个事件,是flume数据流中的一个data object。如果flume的source是文本文件的话,文件中的每一行就会产生一个flume event。

getActions()函数

·         public List<Row> getActions() throws FlumeException {
· List<Row> actions = new LinkedList<Row>();
· if(plCol != null){
· byte[] rowKey;
· try {
· // 配置rowkey,具体靠参考SimpleRowKeyGenerator类
· if (keyType == KeyType.TS) {
· rowKey = SimpleRowKeyGenerator.getTimestampKey(rowPrefix);
· } else if(keyType == KeyType.RANDOM) {
· rowKey = SimpleRowKeyGenerator.getRandomKey(rowPrefix);
· } else if(keyType == KeyType.TSNANO) {
· rowKey = SimpleRowKeyGenerator.getNanoTimestampKey(rowPrefix);
· } else {
· rowKey = SimpleRowKeyGenerator.getUUIDKey(rowPrefix);
· }
· // 创建rowkey的一个put
· Put put = new Put(rowKey);
· // 在put中添加一列数据。columnfamily是cf,colunname是plCol,value是payload。
· // plCol是payloadColumn的byte形式。而payloadColumn初始化于Configure函数,来自于flume的配置文件
· // payload初始化于initalize函数,来自于event
· put.add(cf, plCol, payload);
· actions.add(put);
· } catch (Exception e){
· throw new FlumeException("Could not get row key!", e);
· }
· }
· return actions;
· }

getActions函数,它生成一个put对象实例,put最后插入到hbase中。需要注意的是put实例中所有的数据来源。        plCol来自于payloadColumn, payloadColumn来自于flume的配置文件;cf也是来自于flume配置文件;payload来自于event。

plCol对应hbase中的colum, cf对应hbase中的columnfamily,payload对应hbase中的value。

2、HBasesink---RegexHbaseEventSerializer

RegexHbaseEventSerializer 的源码和 SimpleHbaseEventSerializer 差不多,主要在于以下几个区别:

(1) RegexHbaseEventSerializer.configure(Context context):

此Serializer根据正则可以写入多列:

public void configure(Context context) {
String regex = context.getString(REGEX_CONFIG, REGEX_DEFAULT);  //获取配置文件中的正则表达式,默认是“(.*)”
regexIgnoreCase = context.getBoolean(IGNORE_CASE_CONFIG,
INGORE_CASE_DEFAULT);  //是否忽略大小写
inputPattern = Pattern.compile(regex, Pattern.DOTALL + (regexIgnoreCase ? Pattern.CASE_INSENSITIVE : 0));  //将给定的正则表达式编译到具有给定标志的模式中
String colNameStr = context.getString(COL_NAME_CONFIG, COLUMN_NAME_DEFAULT);  //获取配置文件中的列名
String[] columnNames = colNameStr.split(",");  //分割列名获得列名数组
for (String s: columnNames) {
colNames.add(s.getBytes(Charsets.UTF_8));
}
}

(2) RegexHbaseEventSerializer.getActions()方法

首先会做一些判断匹配成功否?匹配出的个数和指定的列数相同否?,然后是获取rowkey,这里的rowkey是[time in millis]-[random key]-[nonce]三部分组成的字符串。剩下的是依次匹配列组成Put,返回List<Row> actions。

(3) RegexHbaseEventSerializer.getIncrements()

直接返回一个没有数据的List<Increment>,即不设置计数器。

3、AsyncHBasesink---SimpleAsyncHbaseEventSerializer

SimpleAsyncHbaseEventSerializer类和SimpleHbaseEventSerializer的主要区别在于getActions函数。

SimpleAsyncHbaseEventSerializer:getActions()函数

·         public List<PutRequest> getActions() {
· List<PutRequest> actions = new ArrayList<PutRequest>();
· if(payloadColumn != null){
· byte[] rowKey;
· try {
· switch (keyType) {
· case TS:
· rowKey = SimpleRowKeyGenerator.getTimestampKey(rowPrefix);
· break;
· case TSNANO:
· rowKey = SimpleRowKeyGenerator.getNanoTimestampKey(rowPrefix);
· break;
· case RANDOM:
· rowKey = SimpleRowKeyGenerator.getRandomKey(rowPrefix);
· break;
· default:
· rowKey = SimpleRowKeyGenerator.getUUIDKey(rowPrefix);
· break;
· }
· // table是hbase中的表名
· // rowkey是hbase中的rowkey
· // cf是hbase中的columnFamily
· // payloadColum是hbase中的column
· // payload是hbase中的value
· PutRequest putRequest = new PutRequest(table, rowKey, cf,
· payloadColumn, payload);
· actions.add(putRequest);
· } catch (Exception e){
· throw new FlumeException("Could not get row key!", e);
· }
· }
· return actions;
· }

 与SimpleHbaseEventSerializer的getActions的不同,不是产生put实例,而是生成PutRequest实例。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于 2018-01-20
 
 
 
https://blogs.apache.org/flume/entry/streaming_data_into_apache_hbase
 
 
 

Flume-Hbase-Sink针对不同版本flume与HBase的适配研究与经验总结的更多相关文章

  1. flume 自己定义 hbase sink 类

    參考(向原作者致敬) http://ydt619.blog.51cto.com/316163/1230586 https://blogs.apache.org/flume/entry/streamin ...

  2. 自定义flume的hbase sink 的序列化程序

    package com.hello.hbase; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import ...

  3. 低版本Flume兼容高版本elasticsearch

    Flume更新比较慢,而elasticsearch更新非常快所以当涉及更换elasticsearch版本时会出现不兼容问题. apache-flume-1.6.0+elasticsearch1.5.1 ...

  4. [Flume][Kafka]Flume 与 Kakfa结合例子(Kakfa 作为flume 的sink 输出到 Kafka topic)

    Flume 与 Kakfa结合例子(Kakfa 作为flume 的sink 输出到 Kafka topic) 进行准备工作: $sudo mkdir -p /flume/web_spooldir$su ...

  5. 【原创】大叔经验分享(54)flume kudu sink运行一段时间kudu client报错

    flume kudu sink运行一段时间报错: 19/05/05 10:15:56 WARN client.ConnectToCluster: Error receiving a response ...

  6. [bigdata] 使用Flume hdfs sink, hdfs文件未关闭的问题

    现象: 执行mapreduce任务时失败 通过hadoop fsck -openforwrite命令查看发现有文件没有关闭. [root@com ~]# hadoop fsck -openforwri ...

  7. WARN conf.FlumeConfiguration: Could not configure sink sink1 due to: No channel configured for sink: sink1 org.apache.flume.conf.ConfigurationException: No channel configured for sink: sink1

    1.错误如下所示,启动flume采集文件到hdfs案例的时候,出现如下所示的错误: 大概是说No channel configured for sink,所以应该是sink哪里配置出现了错误,百度了一 ...

  8. Flume多Sink方案修正

    在实际项目中采用http://www.cnblogs.com/moonandstar08/p/6091384.html方案进行布署时,由于系统产生的消费比较大按照原方案进行布署时,随着国外局点不断增加 ...

  9. flume 自定义sink

    http://flume.apache.org/FlumeDeveloperGuide.html#sink 看了 还是比较好上手的,简单翻译一下 sink的作用是从 Channel 提取 Event  ...

随机推荐

  1. javascript_08-while 和 do while

    while 和 do while for 当循环次数已知 while 先判断再执行 do while 先执行一次再判断 //1-100 之间所有数字的和 //while // var i = 1; / ...

  2. Pycharm 社区版本Database Navigator 安装教程

    虽然Pycharm有专业版和社区版这两个版本,但是在大多数情况下我们都会选择社区版进行下载安装.为啥呢?因为社区版免费呗,而且能够基本满足我们的日常需求(这也就意味着社区版会比专业版少一些功能).针对 ...

  3. 算法:用户喜好--Map与List配合下的查找

    提示:在算法处理过程中,未必就要将出现在前面的作为关键字检索.比如本题,非得先去检索范围,再去判断范围中key的个数.反其道而行,把输入的数字当作关键字,组成Map package test; imp ...

  4. 2019牛客暑期多校训练营(第九场)All men are brothers——并查集&&组合数

    题意 最初有 $n$ 个人且互不认识,接下来 $m$ 行,每行有 $x,y$,表示 $x$ 和 $y$ 交朋友,朋友关系满足自反性和传递性,每次输出当前选取4个人且互不认识的方案数. 分析 并查集维护 ...

  5. jdk1.8 ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数

    ConcurrentHashMap 的工作原理及代码实现: 相比于1.7版本,它做了两个改进 1.取消了segment分段设计,直接使用Node数组来保存数据,并且采用Node数组元素作为锁来实现每一 ...

  6. struts--CRUD优化(图片上传)

    1.上传方式 上传到指定文件目录,添加服务器与真实目录的映射关系,从而解耦上传文件与tomcat的关系  文件服务器 2.web代码优化 package com.yuan.crud.web; impo ...

  7. spark读HFile对hbase表数据进行分析

    要求:计算hasgj表,计算每天新增mac数量. 因为spark直接扫描hbase表,对hbase集群访问量太大,给集群造成压力,这里考虑用spark读取HFile进行数据分析. 1.建立hasgj表 ...

  8. MongoDB 集群设置集合分片生效及查看集合分片情况

    一.设计DB分片与Collection分片 #连接mongos /opt/mongodb/mongodb-linux-x86_64-2.4.8/bin/mongo  127.0.0.1:27017   ...

  9. MySQL表结构,表空间,段,区,页,MVCC ,undo 事务槽

    索引组织表(IOT表):为什么引入索引组织表,好处在那里,组织结构特点是什么,如何创建,创建IOT的限制LIMIT. IOT是以索引的方式存储的表,表的记录存储在索引中,索引即是数据,索引的KEY为P ...

  10. 070_Shell 脚本对信号的处理,执行脚本后,按键盘 Ctrl+C 无法终止的脚本

    #!/bin/bash#使用 trap 命令可以拦截用户通过键盘或 kill 命令发送过来的信号#使用 kill -l 可以查看 Linux 系统中所有的信号列表,其中 2 代表 Ctrl+C#tra ...