c++访问hbase

一、环境配置
1、  安装thrift

按照官网http://thrift.apache.org/要求及步骤配置安装thrift

2、  取出Hbase文件中的Hbase.thrift文件,是用thrift--gen <language> <Thrift filename>产生所需的cpp文件,将gen_cpp文件复制到工程目录下。其中Hbase.cpp是CPP版的thrift文件中的函数,Hbase_types.cpp是CPP版的thrift文件中的结构。

3、  Hbase启动thrift服务./bin/hbase-daemon.sh start thrift                默认监听地址9090

以上步骤做好之后就可使用C++连接hbase并对其中数据库进行操作了

二、操作步骤
1、  创建连接

boost::shared_ptr<TTransport>socket(new TSocket("host",port(int)));

boost::shared_ptr<TTransport>transport(new TBufferedTransport(socket));

boost::shared_ptr<TProtocol>protocol(new TBinaryProtocol(transport));

HbaseClient client(protocol);

transport->open();    失败返回0并抛出TException错误

 

2、  连接成功后,可以通过client调用Hbase.thrift中定义的函数对hbase进行操作

3、  断开连接

transport->close()     失败抛出TException错误

 

三、HbaseClient类中定义的结构及函数
这些结构和函数都是在Hbase.thrift中定义,C++定义是由thrift读取Hbase.thrift生成的并存放在生成的cpp文件中,若要是用需在工程中包含这些文件

1)、          异常

前三个异常类都包含一个string成员message,不同的函数出错会抛出不同的错误

1、  IOError               操作过程中的一般性错误

2、  IllegalArgument        函数传入参数错误

3、  AlreadyExists     如建表表已存在

4、   TException                   thrift服务的异常类,前三个都是Hbase.thrift中定义,生成cpp文件的定义中前三个都是都是这个异常类的子类

 

2)、          数据类(等于多少表示默认值)

1、  TCell:

1: Stringvalue;   一般是对应列数据

2: I64timestame;

2、  ColumnDescriptor:包含数据库中表的某个列族的信息

  1:string  name,

  2:i32 maxVersions = 3,

  3:string compression = "NONE",

  4:bool inMemory = 0,

  5:string bloomFilterType = "NONE",

  6:i32 bloomFilterVectorSize = 0,

  7:i32 bloomFilterNbHashes = 0,

  8:bool blockCacheEnabled = 0,

  9:i32 timeToLive = -1

3、   TRegionInfo:包含表中某个区域的信息

 1:stringstartKey,

2:string endKey,

 3:i64 id,

 4:stringname,

 5:stringversion

4、   Mutation:         用来更新或删除一个column-value

 1:boolisDelete = 0,

 2:stringcolumn,

 3:stringvalue

5、   BatchMutation:       单列多个mutation的结构

 1:stringrow,

 2:vector<Mutation>mutations

6、   TRowResult:  

 1:stringrow,     rowkey

 2:map<Text, TCell> columns     对应column-value值,Text为列名

7、   TScan:    用以scanner相关函数

 1:optionalstring startRow,                  起始rowkey

 2:optionalstring stopRow,                  终止rowkey

 3:optionali64 timestamp,

 4:optionallist<string> columns,

 5:optionali32 caching,

 6:optionalstring filterString

8、  Typedef ScannerID i32

9、  Typedef    Text(Byte) string

 

3)、          操作函数(除非注明,返回值都为void)

1、  enableTable(string tablename) throws(IOError);           启用一个表

2、  disableTable(string tablename) throws(IOError);           禁用一个表

3、  bool isTableEnabled(string tablename) throws(IOError); 判断表释放启用

4、  compact(string tablenameorregionname) throws(IOError)

5、  majorCompact(string tablenameorregionname) throws(IOError)

4,5调用使hbase合并相应小文件

6、   getTableNames(std::vector<string> &_return);   throws(IOError) 

将用户空间所有表存在_return中

7、   getColumnDescriptors(std::map<Text,ColumnDescriptor> & _return,constText& tableName); throws(IOError),

将表tableName中所有列族信息存在_return中

8、   getTableRegions(std::vector<TRegionInfo>& _return, const Text& tableName);  throws(IOError)

返回表tableName中的Regions信息,返回值在return中

9、   createTable(constText& tableName,conststd::vector<ColumnDescriptor> & columnFamilies);  throws (1:IOError io, 2:IllegalArgument ia,3:AlreadyExists exist)

创建一个表为为tableName,拥有列族columnFamilies的表

10、  deleteTable(constText& tableName);                  throws(IOError)

删除表

11、  get(std::vector<TCell> & _return, const Text& tableName,constText& row, const Text& column);   throws(IOError)

获得表中指定rowkey,指定行的TCell

12、  getVer(std::vector<TCell> & _return, const Text& tableName, constText& row, const Text& column, const int32_t numVersions); throws(IOError)

获得表中指定rowkey,指定列中numVersion个TCell

13、  getVerTs(std::vector<TCell> & _return, const Text& tableName, constText& row, const Text& column, const int64_t timestamp,constint32_t numVersions);         throws(IOError)

获得表中指定rowkey,指定列中numVersions个Tcell,其中Tcell的时间戳要小于或等于给定的时间戳,行不存在返回值空

14、  getRow(std::vector<TRowResult> &_return, const Text& tableName, const Text& row);  throws(IOError)

获得表中指定rowKey最近时间戳的所在行的所有列的数据

15、  getRowWithColumns(std::vector<TRowResult>& _return, const Text& tableName, const Text& row, conststd::vector<Text> & columns);            throws(IOError)

获得表中指定行最近时间戳的所在行的指定列的数据

16、  getRowTs(std::vector<TRowResult> &_return, const Text& tableName, const Text& row, constint64_t timestamp);         throws(IOError)

获得表中指定rowkey,指定时间戳的行的所有列数据

17、  getRowWithColumnsTs(std::vector<TRowResult>& _return, const Text& tableName, const Text& row, conststd::vector<Text> & columns, constint64_t timestamp);              throws(IOError)

获得表中指定rowkey,指定时间戳的行的指定列数据

18、  getRows(std::vector<TRowResult> &_return, const Text& tableName, const std::vector<Text> & rows); throws(IOError)

rows中元素为rowkey,返回表中含有rows中最近时间戳的行数据

19、  getRowsWithColumns(std::vector<TRowResult>& _return, const Text& tableName, const std::vector<Text> & rows,const std::vector<Text> & columns);   throws(IOError)

与18相似,此次返回指定列

20、  getRowsTs(std::vector<TRowResult> &_return, const Text& tableName, const std::vector<Text> & rows,const int64_t timestamp);           throws(IOError)

获得表中指定rowkey,指定时间戳的行

21、  getRowsWithColumnsTs(std::vector<TRowResult>& _return, const Text& tableName, const std::vector<Text> & rows,const std::vector<Text> & columns, const int64_t timestamp);     throws(IOError)

与20相似,此处增加指定列

22、  mutateRow(constText& tableName,const Text& row, const std::vector<Mutation> & mutations);             throws(1:IOError,  2:IllegalArgument)         throws(1:IOError,2:IllegalArgument)

更新或删除指定rowkey的指定列,其中数据使用当时时间作为时间戳,不同mutation插入后时间戳相同

23、  mutateRowTs(constText& tableName,const Text& row, const std::vector<Mutation> & mutations,const int64_t timestamp); throws(1:IOError,2:IllegalArgument)

与22相似,不同在于此处指定了时间戳

24、  mutateRows(constText& tableName,conststd::vector<BatchMutation> & rowBatches); throws(1:IOError, 2:IllegalArgument)

与22相似,输入参数变为可能有多个行

25、  mutateRowsTs(constText& tableName,conststd::vector<BatchMutation> & rowBatches, constint64_t timestamp);        throws(1:IOError, 2:IllegalArgument)

与23相似,输入参数变为可能有多个行

26、  int64_t atomicIncrement(constText& tableName,const Text& row, const Text& column,constint64_t value);           throws(1:IOError,2:IllegalArgument)

指定rowKey制定列的值增加value,返回增加后的值

27、  deleteAll(constText& tableName,const Text& row, const Text& column);                  throws(IOError)

删除表中符合row,column条件的cells

28、  deleteAllTs(constText& tableName,const Text& row, const Text& column,constint64_t timestamp);      throws(IOError)

删除表中符合row,column条件并且时间戳等于或早于给定时间戳的cells

29、  deleteAllRow(constText& tableName,const Text& row); throws(IOError)

完全删除指定rowkey的cell

30、  deleteAllRowTs(constText& tableName,const Text& row, const int64_t timestamp);        throws(IOError)

完全删除指定rowkey,并且时间戳等于或早于给定时间戳的cells

31、  ScannerID scannerOpenWithScan(const Text& tableName,constTScan& scan);      throws(IOError)

在表上打开一个scanner,scanner条件在一个TScan结构的传入参数定义,返回ScannerID

32、  ScannerID scannerOpen(constText& tableName,const Text& startRow, const std::vector<Text> & columns);           throws(IOError)

在表上打开一个scanner,输入参数为这个scanner的起始rowkey和需要访问的列,返回scannerID。

33、  ScannerID scannerOpenWithStop(const Text& tableName,constText& startRow, const Text& stopRow,const std::vector<Text> & columns);            throws(IOError)

与32相似,但是多指定了scanner的中止行

34、  ScannerID scannerOpenWithPrefix(const Text& tableName,constText& startAndPrefix, conststd::vector<Text> & columns)  throws(IOError)

这个scanner的rowkey有指定的前缀

35、  ScannerID scannerOpenTs(constText& tableName,const Text& startRow, const std::vector<Text> & columns,const int64_t timestamp);                  throws(IOError)

这个scanner只返回指定时间戳的数据

36、  ScannerID scannerOpenWithStopTs(const Text& tableName,constText& startRow, const Text& stopRow,const std::vector<Text> & columns, const int64_t timestamp);                       throws(IOError)

与33相似,但多了一个条件,这个scanner只返回指定时间戳的行数据

37、  scannerGet(std::vector<TRowResult> &_return, const ScannerID id);                throws(1:IOError,2:IllegalArgument)

获得ScannerID这个scanner的当前行数据并且让他指向下行,若没数据读了则运行之后_return为空

38、  scannerGetList(std::vector<TRowResult>& _return, const ScannerID id, const int32_t nbRows);               throws(1:IOError, 2:IllegalArgument)

与37相似,不过一次返回多行

39、  scannerClose(constScannerID id);

关闭一个scanner


四、实例
#include "THBaseService.h" 
#include <config.h> 
#include <vector> 
#include <transport/TSocket.h> 
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h> 
#include <string.h>
 
using namespace std; 
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport; 
using namespaceapache::hadoop::hbase::thrift2; 
 
using boost::shared_ptr; 
 
 
int readdb(int argc, char** argv)
{
 
   fprintf(stderr, "readdb start\n");
       
    int port= atoi(argv[2]);
   boost::shared_ptr<TSocket> socket(new TSocket(argv[1],port)); 
   boost::shared_ptr<TTransport> transport(newTBufferedTransport(socket)); 
   boost::shared_ptr<TProtocol> protocol(newTBinaryProtocol(transport)); 
    try {
 
       transport->open(); 
       printf("open\n");
       THBaseServiceClient client(protocol); 
       TResult tresult; 
        TGetget;
 
 
       std::vector<TColumnValue> cvs;
       const std::string table("m"); 
       const std::string thisrow="ss-9-8";
       
 
 
       //get data
       get.__set_row(thisrow); 
        boolbe = client.exists(table,get);
       
       printf("exists result value = %d\n", be);
 
 
       client.get(tresult,table,get);
       vector<TColumnValue> list=tresult.columnValues; 
       std::vector<TColumnValue>::const_iterator iter; 
       for(iter=list.begin();iter!=list.end();iter++) { 
           printf("%s, %s,%s\n",(*iter).family.c_str(),(*iter).qualifier.c_str(),(*iter).value.c_str());
        }
       transport->close(); 
        printf("close\n"); 
     } catch(const TException &tx) {
              fprintf(stderr,"error:%s\n", tx.what());
     }
   fprintf(stderr, "readdb stop\n");
    return0;
}
 
int writedb(int argc, char** argv)
{
   fprintf(stderr, "writedb start\n");
    int port= atoi(argv[2]);
   boost::shared_ptr<TSocket> socket(new TSocket(argv[1], port));
   boost::shared_ptr<TTransport> transport(newTBufferedTransport(socket));
   boost::shared_ptr<TProtocol> protocol(newTBinaryProtocol(transport));
    try
    {
        charbuf[128];
       transport->open(); 
       printf("open\n");
       THBaseServiceClient client(protocol); 
       TResult tresult; 
        TGetget;
       std::vector<TPut> puts;
       const std::string table("m"); 
        for(inti = 0; i < 10; i++) {
           fprintf(stderr, "%d, ", i);
           for(int j = 0; j < 10; j++) {
               TPut put;
               std::vector<TColumnValue> cvs;
               //put data
               sprintf(buf, "ss-%d-%d", i, j);
               const std::string thisrow(buf);
               put.__set_row(thisrow);
               TColumnValue tcv;
               tcv.__set_family("info");
               tcv.__set_qualifier("age");
               sprintf(buf, "%d", i * j);
                tcv.__set_value(buf);
               cvs.insert(cvs.end(), tcv);
               put.__set_columnValues(cvs);
               puts.insert(puts.end(), put);
           }
           client.putMultiple(table, puts);
           puts.clear();
        }
 
 
       transport->close(); 
       printf("close\n"); 
    } catch(const TException &tx) {
              fprintf(stderr,"error:%s\n", tx.what());
    }
   fprintf(stderr, "writedb stop\n");
    return0;
}
 
 
int main(int argc, char **argv) { 
    if(argc!= 3) {
       fprintf(stderr, "param  is:XX ip port\n");
       return -1;
    }
//   writedb(argc, argv);
   readdb(argc, argv);
    return0; 

//Makefile文件
THRIFT_DIR = /usr/local/include/thrift
LIB_DIR = /usr/local/lib
 
GEN_SRC = ./gen-cpp/hbase_types.cpp ./gen-cpp/hbase_constants.cpp./gen-cpp/THBaseService.cpp
 
.PHONY: clean help
 
default: HbaseClient
HbaseClient: HbaseClient.cpp
       g++  -o HbaseClient -I${THRIFT_DIR}  -I./gen-cpp -L${LIB_DIR}-Wl,-rpath,${LIB_DIR} HbaseClient.cpp ${GEN_SRC} -lthrift -g
 
clean:
       rm -rfHbaseClient
 
help:
       $(warning"Makefile for C++ Hbase Thrift HbaseClient. Modify THRIFT_DIR and LIB_DIRin the \
file to point to correct locations. See$${HBASE_ROOT}/hbase-examples/README.txt for \
details.")
       @:

五、常见问题
1、客户端连接超时问题。
hbase没有提供直接供c++的访问方法,而是通过thrift2服务提供。如需要通过c++访问hbase就需要hbase开启thrift server。
开启方法:
$HBASE_HOME/bin/hbase-daemon.sh start thrift2 -t 60000

thrift server默认开启9090端口。hbase对客户端读取有超时策略,默认1分钟,启动时可通过-t参数指定超时时长,超时后hbase客户端会关闭连接,这时如果用原连接读取数据会产生Broken pipe的异常。其他参数可通过查看thrift server源码了解 。

2、HBase客户端优化
默认的HBase客户端的参数配置是没有做过优化的,所以对于低延时响应的HBase集群,需要对客户端的参数进行优化。
hbase.rpc.timeout
以毫秒计算的所有HBase RPC超时,默认为60s。 
该参数表示一次RPC请求的超时时间。如果某次RPC时间超过该值,客户端就会主动关闭socket。 
如果经常出现java.io.IOException: Connection reset by peer异常问题,估计HBase集群出现了大量高并发读写业务或者服务器端发生了比较严重的Full GC等问题,导致某些请求无法得到及时处理,超过了设置的时间间隔。 
根据实际情况,可以修改为5000,即5s。

hbase.client.retries.number
客户端重试最大次数。所有操作所使用的最大次数,例如,从根 RegionServer 获取根区域、获取单元格的值和启动行更新。 
默认为35,可以设置为3。

hbase.client.pause
通用客户端暂停时间值(重试的休眠时间)。重试get失败或区域查找等操作前,经常使用的等待时间段。 
HBase1.1版本开始此值默认为100ms,比较合理,如果你的版本不是,建议修改此值为100ms。

zookeeper.recovery.retry
zookeeper的重试次数,可调整为3次,zookeeper不轻易挂,且如果HBase集群出问题了,每次重试均会对zookeeper进行重试操作。 
zookeeper的重试总次数是:

 hbase.client.retries.number * zookeeper.recovery.retry。
1
并且每次重试的休眠时间均会呈2的指数级增长,每次访问HBase均会重试,在一次HBase操作中如果涉及多次zookeeper访问,则如果zookeeper不可用,则会出现很多次的zookeeper重试,非常浪费时间。

zookeeper.recovery.retry.intervalmill
zookeeper重试的休眠时间,默认为1s,可以减少,比如:200ms。

hbase.client.operation.timeout
该参数表示HBase客户端发起一次数据操作直至得到响应之间总的超时时间,数据操作类型包括get、append、increment、delete、put等。很显然,hbase.rpc.timeout表示一次RPC的超时时间,而hbase.client.operation.timeout则表示一次操作的超时时间,有可能包含多个RPC请求。 
举个例子说明,比如一次Put请求,客户端首先会将请求封装为一个caller对象,该对象发送RPC请求到服务器,假如此时因为服务器端正好发生了严重的Full GC,导致这次RPC时间超时引起SocketTimeoutException,对应的就是hbase.rpc.timeout。那假如caller对象发送RPC请求之后刚好发生网络抖动,进而抛出网络异常,HBase客户端就会进行重试,重试多次之后如果总操作时间超时引起SocketTimeoutException,对应的就是hbase.client.operation.timeout。 
默认为1200000,可以设置为30000,即30s。

hbase.regionserver.lease.period
hbase.client.operation.timeout参数规定的超时基本涉及到了HBase所有的数据操作,唯独没有scan操作。然而scan操作却是最有可能发生超时的,也是用户最为关心的。HBase特别考虑到了这点,并提供了一个单独的超时参数进行设置:hbase.client.scanner.timeout.period。 
此参数指scan查询时每次与RegionServer交互的超时时间。 
默认为60s,可不调整。

3、client应用读写分离
    读和写分离,位于不同的tomcat实例,数据先写入redis队列,再异步写入hbase,如果写失败再回存redis队列,先读redis缓存的数据(如果有缓存,需要注意这里的redis缓存不是redis队列),如果没有读到再读hbase。
    当hbase集群不可用,或者某个RS不可用时,因为HBase的重试次数和超时时间均比较大(为保证正常的业务访问,不可能调整到比较小的值,如果一个RS挂了,一次读或者写,经过若干重试和超时可能会持续几十秒,或者几分钟),所以一次操作可能会持续很长时间,导致tomcat线程被一个请求长时间占用,tomcat的线程数有限,会被快速占完,导致没有空余线程做其它操作,读写分离后,写由于采用先写redis队列,再异步写hbase,因此不会出现tomcat线程被占满的问题, 应用还可以提供写服务,如果是充值等业务,则不会损失收入,并且读服务出现tomcat线程被占满的时间也会变长一些,如果运维介入及时,则读服务影响也比较有限。

hbase优化操作与建议 https://www.cnblogs.com/xjh713/p/7750134.html

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读