天天看點

Cloudera Manager HBase Thrift 接口 Go/Python用戶端

背景

最近有個需求是寫一個資料查詢接口,資料存放在CDH搭建的Hadoop叢集HBase中。一直以來是個堅定的Pythoner(其實是懶),不過今年以來逐漸接觸和嘗試Go之後覺得很對胃口,再加上公司令人作嘔的運維管控機制,使用Go的項目靜态編譯為單個檔案,可以将運維依賴降到最低,是以對Go越發心儀。

用net/http配合gorilla/mux包輕車熟路的實作了一套簡單的查詢接口之後,本以為輕松完成HBase的DAL就可以打包測試了,但是要是coding都這麼一帆風順那就顯示不出什麼技(bi)術(ge)了。是以意料之外又情理之中的,用Go擷取HBase中并沒有那麼簡單。

本以為HBase這麼成熟的資料庫,Go會有很友善實用的官方或第三方庫友善通路,但在搜尋一番之後,發現隻有兩個選擇:HBase提供的Thrift,以及這個 仍被開發者标記為Beta版本的第三方庫GoHbase。在起初調試Thrift始終無果的情況下,筆者嘗試了GoHbase,使用簡單,可以成功擷取HBase資料。考慮到這是線上的項目,本着認(zhe)真(teng)負(dao)責(di)的态度,經過了又一番調試,總算用Thrift達成了目的,下面流水賬記錄一下具體過程。

使用的軟體環境:

  • go version go1.7.4 linux/amd64 & windows/amd64*
  • Thrift version 0.10.0*
  • HBase 1.2.0-cdh5.7.2

步驟

  1. 确定HBase安裝目錄,啟動指令
  2. 用Thrift生成HBase SDK
  3. 實作用戶端代碼

具體過程

查詢HBase目錄及運作指令

HBase提供了兩套thrift接口,首先要确定啟動的hbase thrift server使用了哪套接口, 比如我的:

Cloudera Manager HBase Thrift 接口 Go/Python用戶端

是第一套接口,如果參數是thrift2,就是第二套接口。

進入HBase的目錄,找到Thrift檔案:

[[email protected] thrift]$ ls -l /opt/cloudera/parcels/CDH/lib/hbase/include/thrift

total 44

-rw-r–r– 1 root root 24870 Jul 23 2016 hbase1.thrift

-rw-r–r– 1 root root 15126 Jul 23 2016 hbase2.thrift

用Thrift生成代碼

在上一步中找到對應的thrift檔案,将檔案拷貝到個人目錄下,運作:

thrift -out . -r hbase–gen go ${THRIFT}

生成的代碼目錄如下:

Cloudera Manager HBase Thrift 接口 Go/Python用戶端

其中hbase-remote目錄為生成的用戶端測試代碼,但如果直接運作,會得到一堆報錯:

..\hbase1.go:1662: cannot use temp (type Text) as type string in assignment

..\hbase1.go:11229: cannot use temp (type Text) as type string in assignment

..\hbase1.go:12252: cannot use temp (type Text) as type string in assignment

..\hbase1.go:12669: cannot use temp (type Text) as type string in assignment

..\hbase1.go:13121: cannot use temp (type Text) as type string in assignment

..\hbase1.go:13531: cannot use temp (type Text) as type string in assignment

..\hbase1.go:13925: cannot use temp (type Text) as type string in assignment

..\hbase1.go:14330: cannot use temp (type Text) as type string in assignment

..\hbase1.go:14759: cannot use temp (type Text) as type string in assignment

..\hbase1.go:15173: cannot use temp (type Text) as type string in assignment

..\hbase1.go:15173: too many errors

錯誤: 程序退出代碼 2.

可能是thrift版本不相容造成的,在代碼中發現如下定義:

type Text []byte

定位到報錯的位置:

var _key1 string

if v, err := iprot.ReadString(); err != nil {

return thrift.PrependError(“error reading field 0: “, err)

} else {

temp := Text(v)

_key1 = temp

}

發現temp被指派給string類型的_key1沒有做類型轉換,手動把所有報錯位置都修改如下:

temp := Text(v)

_key1 = string(temp)

修改代碼中host和port為實際位址,再次運作:

[[email protected] hbase-remote]$ go run hbase-remote.go

Usage of /tmp/go-build890271332/command-line-arguments/_obj/exe/hbase-remote

[-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2…]]:

-P string Specify the protocol (binary, compact, simplejson, json) (default “binary”)

-framed Use framed transport

-h string Specify host and port (default “10.59.74.135”)

-http Use http

-p int Specify port (default 9090)

-u string Specify the url

…….

錯誤解決。

現在可以将生成的hbase目錄拷貝到$GOPATH/src中。

實作用戶端

簡單的示例代碼如下:

package main

import (
    "fmt"
    "net"
    "os"
    "hbase1"
    "github.com/apache/thrift/lib/go/thrift"
)

func main() {
    host := "10.59.74.135"
    port := "9090"

    trans, err := thrift.NewTSocket(net.JoinHostPort(host, port))
    if err != nil {
        fmt.Println("Build socked failed: ", err)
        os.Exit)
    }

    defer trans.Close()
    var protocolFactory thrift.TProtocolFactory
    //protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
    protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()

    client := hbase1.NewHbaseClientFactory(trans, protocolFactory)
    if err := trans.Open(); err != nil {
        fmt.Println("Opening socket failed: ", err)
        os.Exit)
    }

    tableName := "agentBasicInfo" // tablename
    rowKey := "1970010121012971" // rowkey
    family := "basicinfo:entry_date" // column

    tables, err := client.GetTableNames()
    if err != nil {
        fmt.Println("Get tables failed: ", err)
        os.Exit)
    }
    for _, table := range tables {
        fmt.Println("table: ", string(table))
    }

    fmt.Println("-------------------")
    fmt.Printf("trying to get table: {%s}, rowkey: {%s}\n", tableName, rowKey)

    //attr := map[string]hbase1.Text {"basicinfo":[]byte("entry_date")}
    data, err := client.Get([]byte(tableName), []byte(rowKey), []byte(family), nil)
    if err != nil {
        fmt.Println("Get data failed: ", err)
    }
    for _, ele := range data {
        fmt.Println("value: ", ele.Timestamp, " ", string(ele.Value))
    }
}
           

執行結果如下:

[[email protected] test_hbase]$ go run test_thrift.go

table: KYLIN_010EV7WZQ6

table: KYLIN_228LAP2P5A

table: KYLIN_3AYUR4WPJW

table: KYLIN_4DX8LTMC7A

table: KYLIN_4XR1LT20V4

table: KYLIN_959ZEKZBEM

table: KYLIN_9OHU8KSWI3

table: KYLIN_A6DW68YNOX

table: KYLIN_A6JKAAU8KS

table: KYLIN_BB5KKOWPCN

table: KYLIN_BUNDHMMD78

table: KYLIN_BZTUAMVLK6

table: KYLIN_CMQF0PAX8T

table: KYLIN_DK8AAXFNR7

table: KYLIN_DPFEWKDP5N

……

Python用戶端

其實Hbase源碼包中已經有很多語言用戶端的示例代碼

[[email protected] repos]$ ls hbase-1.2.0-cdh5.7.2/hbase-examples/src/main

cpp java perl php protobuf python ruby sh

python用戶端示例檔案:

[email protected] python]$ tree .
├── thrift1
│   ├── DemoClient.py
│   └── gen-py
│       └── hbase
│           ├── constants.py
│           ├── Hbase.py
│           ├── Hbase.pyc
│           ├── Hbase-remote
│           ├── __init__.py
│           ├── __init__.pyc
│           ├── ttypes.py
│           └── ttypes.pyc
└── thrift2
    ├── DemoClient.py
    └── gen-py
        └── hbase
            ├── constants.py
            ├── __init__.py
            ├── __init__.pyc
            ├── THBaseService.py
            ├── THBaseService.pyc
            ├── THBaseService-remote
            ├── ttypes.py
            └── ttypes.pyc
           

分别對應兩個版本的Thrift接口,參考其中DemoClient.py,即可實作自己的HBase用戶端。

目錄

    • 背景
      • 步驟
    • 具體過程
      • 查詢HBase目錄及運作指令
      • 用Thrift生成代碼
      • 實作用戶端
      • Python用戶端
      • 目錄

繼續閱讀