
封面题图:安提基特拉(Antikythera)机械复原想象图,该机械是为了计算天体在天空中的位置而设计的古希腊青铜机器,属于模拟计算机【百度百科】。该机器于1900年在希腊安提基特拉岛附近的安提基特拉沉船里被发现。制造年代约在西元前150年到100年之间。现藏于希腊国家考古博物馆。引用该图是感叹于远古时代人类技术成就,技术是人类超越自身局限性的工具,我们要正视人类文明的脆弱性与对技术的依赖性,在利用技术同时,也要避免匍匐在技术力量面前,完全成为其附庸。
注:本文同步以“古翠码翁” ID发布于CSDN Web3社区,转载请注明出处。
一、引言
最近在项目实践中,涉及到使用Go语言操作HBase数据库,本文是相关的经验总结。
用程序操作HBase数据有多种方法,包括HBase提供的RPC接口,Apache Thrift工具或者Apache Phoenix工具等。其中用原生RPC接口方式简单直接,不需要部署额外第三方组件,直接引用相应程序开发包即可。
Go语言具有丰富的生态环境,除了标准库,还有各种开源开发包,安装方便,如果开发包不满足业务需求还可以直接修改源代码来适配。
然而在使用中,上述几个开源库操作工作环境部署的HBase时均出现各种问题,有不同的服务端程序报错信息出现。经过排查分析,发现一个可能原因是笔者使用的HBase版本是2.0.x,这个版本相对于HBase1.0.x有较大升级,可能存在一些底层接口不兼容。之所以说是可能原因,是因为这些开源库的兼容版本要求只给出了>HBase0.9x版,并没有给出说明是否兼容2.0以上版本。
解决办法通常有三个——其一,重新部署HBase,降低到相应版本;其二,继续寻找支持2.0以上版本的库;其三,修改库兼容工作环境的HBase版本。重新部署环境依赖于运维同事,能否找到兼容的库基于之前经验不确定性较高,求人不如求己,于是选择第三个方法,直接Hack现有库以兼容当前版本HBase。
这里选择pingcap/go-hbase库作为修改对象,原因是代码结构比较清晰,接口简单,使用方便。按照这个库的官方说法:Derived from Lazyshot/go-hbase. Add some new features and fix some bugs.
这个库源代码库地址:https://github.com/pingcap/go-hbase 代码树形结构图如图1所示,可见文件并不多,并不是一个复杂项目。
├── action.go ├── action_test.go ├── admin.go ├── admin_test.go ├── call.go ├── client.go ├── client_ops.go ├── client_test.go ├── column.go ├── column_test.go ├── conn.go ├── del.go ├── del_test.go ├── get.go ├── get_test.go ├── iohelper │ ├── multireader.go │ ├── pbbuffer.go │ └── utils.go ├── LICENSE ├── proto │ ├── AccessControl.pb.go │ ├── Admin.pb.go │ ├── Aggregate.pb.go │ ├── Authentication.pb.go │ ├── Cell.pb.go │ ├── Client.pb.go │ ├── ClusterId.pb.go │ ├── ClusterStatus.pb.go │ ├── Comparator.pb.go │ ├── Encryption.pb.go │ ├── ErrorHandling.pb.go │ ├── Filter.pb.go │ ├── FS.pb.go │ ├── HBase.pb.go │ ├── HFile.pb.go │ ├── LoadBalancer.pb.go │ ├── MapReduce.pb.go │ ├── Master.pb.go │ ├── MultiRowMutation.pb.go │ ├── RegionServerStatus.pb.go │ ├── RowProcessor.pb.go │ ├── RPC.pb.go │ ├── SecureBulkLoad.pb.go │ ├── Snapshot.pb.go │ ├── Tracing.pb.go │ ├── VisibilityLabels.pb.go │ ├── WAL.pb.go │ └── ZooKeeper.pb.go ├── protobuf │ ├── AccessControl.proto │ ├── Admin.proto │ ├── Aggregate.proto │ ├── Authentication.proto │ ├── Cell.proto │ ├── Client.proto │ ├── ClusterId.proto │ ├── ClusterStatus.proto │ ├── Comparator.proto │ ├── Encryption.proto │ ├── ErrorHandling.proto │ ├── Filter.proto │ ├── FS.proto │ ├── HBase.proto │ ├── HFile.proto │ ├── LoadBalancer.proto │ ├── MapReduce.proto │ ├── Master.proto │ ├── MultiRowMutation.proto │ ├── RegionServerStatus.proto │ ├── RowProcessor.proto │ ├── RPC.proto │ ├── SecureBulkLoad.proto │ ├── Snapshot.proto │ ├── Tracing.proto │ ├── VisibilityLabels.proto │ ├── WAL.proto │ └── ZooKeeper.proto ├── put.go ├── put_test.go ├── README.md ├── result.go ├── result_test.go ├── scan.go ├── scan_test.go ├── service_call.go ├── types.go └── utils.go |
图1. 代码树形结构
二、实现步骤
2.1 源库错误提示
在确保已经安装好go开发环境前提下,安装go-hbase开发包,运行 go get github.com/pingcap/go-hbase,默认这个包会下载到 $GOPATH/pkg/mod/github.com/pingcap/go-hbase@xxxx 路径下
安装好后,开发一个简单Demo,重现一下错误。demo.go代码图2所示,zkHosts是zookeeper集群节点地址,根据实际情况设置,zkRoot是hbase所在根路径,也需要根据实际情况设置,这里是/hbase-unsecure。程序工作很简单,连接HBase,向"demo_tab"数据表的"info"列簇的列"val_1”和"val_2"分别写入数据。
// demo.go package main import ( func main() { log.Fatal(err) } ~ |
图2. demo.go 代码 |

图3. 返回错误提示,协议不兼容
2.2 修改代码解决问题
/* 注释掉对 GetRequest 请求的调用 call := newCall(&proto.GetRequest{ Region: &proto.RegionSpecifier{ Type: proto.RegionSpecifier_REGION_NAME.Enum(), Value: metaRegionName, }, Get: &proto.Get{ Row: regionRow, Column: []*proto.Column{&proto.Column{ Family: []byte("info"), }}, ClosestRowBefore: pb.Bool(true), }, }) */ // 换成对 ScanRequest的调用 call := newCall(&proto.ScanRequest{ Region: &proto.RegionSpecifier{ Type: proto.RegionSpecifier_REGION_NAME.Enum(), Value: metaRegionName, }, Scan: &proto.Scan{ Reversed: pb.Bool(true), StartRow: regionRow, Column: []*proto.Column{&proto.Column{ Family: []byte("info"), }}, }, NumberOfRows: pb.Uint32(1), }) |
图4. 在LocateRegion中添加ScanRequest发送请求

图5. LocateRegion添加ScanRequest请求截图
2.2.2 修改片段之二
switch r := response.(type) { case *proto.GetResponse: case *exception: case *proto.ScanResponse: // 添加对ScanResponse的case处理 default: } |
图6. 添加ScanResponse数据处理部分
2.2.3 修改片段之三
func (c *connection) writeConnectionHeader() error { EffectiveUser: pb.String("hbase"), } |
图7. 在go-hbase/conn.go中设置EffectiveUser
三、测试结果
yyyy/mm/dd hh:mm:ss client.go:169: [debug] connect root region server... host_name:"zk-node4" port:16020 start_code:1655113251448
Put data successfully!

图8. HBase中数据表scan操作返回的结果




