Flume-Hbase-Sink针对不同版本flume与HBase的适配研究与经验总结
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 删除。
Flume-Hbase-Sink针对不同版本flume与HBase的适配研究与经验总结的更多相关文章
- flume 自己定义 hbase sink 类
參考(向原作者致敬) http://ydt619.blog.51cto.com/316163/1230586 https://blogs.apache.org/flume/entry/streamin ...
- 自定义flume的hbase sink 的序列化程序
package com.hello.hbase; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import ...
- 低版本Flume兼容高版本elasticsearch
Flume更新比较慢,而elasticsearch更新非常快所以当涉及更换elasticsearch版本时会出现不兼容问题. apache-flume-1.6.0+elasticsearch1.5.1 ...
- [Flume][Kafka]Flume 与 Kakfa结合例子(Kakfa 作为flume 的sink 输出到 Kafka topic)
Flume 与 Kakfa结合例子(Kakfa 作为flume 的sink 输出到 Kafka topic) 进行准备工作: $sudo mkdir -p /flume/web_spooldir$su ...
- 【原创】大叔经验分享(54)flume kudu sink运行一段时间kudu client报错
flume kudu sink运行一段时间报错: 19/05/05 10:15:56 WARN client.ConnectToCluster: Error receiving a response ...
- [bigdata] 使用Flume hdfs sink, hdfs文件未关闭的问题
现象: 执行mapreduce任务时失败 通过hadoop fsck -openforwrite命令查看发现有文件没有关闭. [root@com ~]# hadoop fsck -openforwri ...
- 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哪里配置出现了错误,百度了一 ...
- Flume多Sink方案修正
在实际项目中采用http://www.cnblogs.com/moonandstar08/p/6091384.html方案进行布署时,由于系统产生的消费比较大按照原方案进行布署时,随着国外局点不断增加 ...
- flume 自定义sink
http://flume.apache.org/FlumeDeveloperGuide.html#sink 看了 还是比较好上手的,简单翻译一下 sink的作用是从 Channel 提取 Event ...
随机推荐
- Spark学习笔记3——RDD(下)
目录 Spark学习笔记3--RDD(下) 向Spark传递函数 通过匿名内部类 通过具名类传递 通过带参数的 Java 函数类传递 通过 lambda 表达式传递(仅限于 Java 8 及以上) 常 ...
- (坑爹错误)记录prometheus中配置alertmanager.yml一次报错
global: resolve_timeout: 5m #处理超时时间,默认为5min smtp_smarthost: 'smtp.sina.com:25' # 邮箱smtp服务器代理 smtp_fr ...
- [476] Database Mail is not enabled for agent notifications. Cannot send e-mail to
配置完DB Mail后JOB的的通知邮件不能发送,日志报错476] Database Mail is not enabled for agent notifications. Cannot send ...
- sql的时间格式
sql中的时间格式转换主要有:date_format函数,str_to_date函数 1. 首先选择一个数据库 use db_name; 2. 显示当前时区的时间: SELECT NOW(); 3. ...
- Mysql:[Err] 1630
Mysql错误 [Err] 1630 . 错误 1630 [SQL]select date_format(now (),'%Y%m%d'); - FUNCTION db.now does not ex ...
- Python语言程序设计:Lab5
Programming Create a Class Student which should have the following information:Parameters passed to ...
- 容器自动化(一):docker基础(上)
一,Docker简介,功能特性与应用场景 1.1 Docker简介 Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上 ...
- Loadrunner 计算保留两位小数不四舍五入
有时候在测试过程中会截取返回值,当你截取的值不是最终的值,需要进行计算后才能使用并且需要保留两位小数,不进行四舍五入的计算: 此时 我使用了各种办法,但是最终我采用了一种最直接,最暴力的方法就是先乘后 ...
- Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释
原题链接 无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...
- C++——构造和析构函数
现在学习进入第三阶段,对c++要有更深入的学习,关于构造函数和析构函数这一块需要总结一下,来深刻理解这两个函数的意义. 什么是构造函数和析构函数呢呢?听着就很高大上,但是要从心里藐视它.就像自然万物有 ...