問題
在通過Go
連接hbase
的過程中, 發現 get
操作可以查到數據, 但是scanner
命令訪問數據失敗, 也沒有報錯, 就是單純的查不到數據. 而且Python
PHP
都一切正常.
這裏簡單複述一下我出現問題的情況, 安裝過程和網上大部分內容一致, 這裏簡單列一下, 只是爲了查詢問題時參考安裝過程的差異:
# 安裝hbase
wget -O /opt/hbase.tar.gz https://dlcdn.apache.org/hbase/2.4.11/hbase-2.4.11-bin.tar.gz
tar -xzf /opt/hbase.tar.gz -C /opt/
rm /opt/hbase.tar.gz
# 安裝thrift
apt install -y libboost-dev \
libboost-test-dev \
libboost-program-options-dev \
libevent-dev \
automake \
libtool \
flex \
bison \
pkg-config \
g++ \
libssl-dev \
gcc \
autoconf \
make \
&& wget -O /opt/thrift.tar.gz https://dlcdn.apache.org/thrift/0.16.0/thrift-0.16.0.tar.gz \
&& tar -xzf /opt/thrift.tar.gz -C /opt \
&& rm /opt/thrift.tar.gz \
&& cd /opt/thrift-0.16.0 \
&& ./configure \
&& make \
&& make install
# 測試使用, 將hbase服務改爲單機模式
cat > /opt/hbase-2.4.11/conf/hbase-site.xml <<- EOF
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- hbase存放數據目錄 -->
<property>
<name>hbase.rootdir</name>
<value>file:///data/hbase</value>
</property>
<!-- ZooKeeper數據文件路徑 -->
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/data/zookeeper</value>
</property>
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
</configuration>
EOF
# 啓動hbase服務
/opt/hbase-2.4.11/bin/start-hbase.sh
# 啓動htrift服務
/opt/hbase-2.4.11/bin/hbase-daemon.sh start thrift2 -p 9091
# 生成go和python的thrift工具文件
wget -O ~/hbase_src.tar.gz https://dlcdn.apache.org/hbase/2.4.11/hbase-2.4.11-src.tar.gz
tar -xzf ~/hbase_src.tar.gz -C ~
cd ~/hbase-2.4.11/hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift2
thrift --gen go hbase.thrift
thrift --gen py hbase.thrift
服務啓動後, 使用GO
進行查詢(其中test表已經提前放入數據):
transport, err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1", "9091"))
if err != nil {
panic(err)
}
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
client := hbase.NewTHBaseServiceClientFactory(transport, protocolFactory)
transport.Open()
scan := &hbase.TScan{}
ret, err := client.GetScannerResults(context.Background(), []byte("test"), scan, 100)
if err != nil {
panic(err)
}
fmt.Println("scanner result")
fmt.Println(ret)
發現使用go查詢不到數據???這個就奇怪了, 因此開始進行檢查(簡單復現一下問題查詢步驟).
原因追溯
首先, 在使用GO
查詢之前已經使用hbase shell
連接過了, 因此可以確定不是hbase
啓動失敗的問題. 因此思考各種問題逐一排查.
端口未開啓
使用命令telnet 127.0.0.1 9091
進行驗證, 端口沒有問題, 可以排除.
是否僅Go有問題
使用python
php
進行連接並訪問相同方法, 發現是有返回數據的, Python
代碼:
from thrift.transport import TSocket
from thrift.protocol import TBinaryProtocol
from thrift.transport import TTransport
from hbase import THBaseService
from hbase.ttypes import TScan
if __name__ == '__main__':
transport = TTransport.TBufferedTransport(TSocket.TSocket('127.0.0.1', 9091))
protocol = TBinaryProtocol.TBinaryProtocolAccelerated(transport)
client = THBaseService.Client(protocol)
transport.open()
# 使用 client 實例進行操作
ret = client.getScannerResults(
table="test",
tscan=TScan(),
numRows=100
)
print(ret)
transport.close()
由此可以證明, thrift
也沒有問題, 一定是Go
在連接的時候出了問題.
是否Go所有方法都有問題
嘗試調用thrift
其他方法, 判斷是僅scanner
方法有問題, 還是所有方法都有問題:
tGet := hbase.TGet{
Row: []byte("row1"), // row1 數據是存在的
}
getRet, err := client.Get(context.Background(), []byte("test"), &tGet)
if err != nil {
panic(err)
}
fmt.Println("get result")
fmt.Println(getRet)
查詢後發現, Get
方法是可以獲取到數據的, 那麼, 就是說僅Go
的GetScannerResults
方法是有問題的???
當問題追溯到這裏的時候, 我已經有些懵了, 嘗試這谷歌查找原因, 各種關鍵詞都試過了, 沒有找到想要的答案(這也是爲什麼我要把這個錯記下來, 因爲沒查到啊)
抓包比較差異
找了半天沒有找到問題, 既然Python
可以查到數據 而Go
查不到, 那麼, 就可以嘗試抓包比較兩者差異了嘛.
依然使用上面的GetScannerResults
, 分別對python
和go
進行抓包分析, 通過命令tmpdump port 9091 -w thrift.cap
:
可以看到, 其中的thrift
請求也能夠被wireshark
識別了, 那就太好了, 不用比較二進制了, 來看一下兩個請求的區別:
Python
請求如下:
Go
請求如下:
其中, 有一個字段兩個是明顯不一樣的, 我很貼心的用紅框圈出來了, 那麼, 問題來了, 這個字段是什麼呢? 我把上面的hbase.thrift
文件中的一部分拿出來, 就很明顯了:
/**
* Any timestamps in the columns are ignored but the colFamTimeRangeMap included, use timeRange to select by timestamp.
* Max versions defaults to 1.
*/
struct TScan {
1: optional binary startRow,
2: optional binary stopRow,
3: optional list<TColumn> columns
4: optional i32 caching,
5: optional i32 maxVersions=1,
6: optional TTimeRange timeRange,
7: optional binary filterString,
8: optional i32 batchSize,
9: optional map<binary, binary> attributes
10: optional TAuthorization authorizations
11: optional bool reversed
12: optional bool cacheBlocks
13: optional map<binary,TTimeRange> colFamTimeRangeMap
14: optional TReadType readType
15: optional i32 limit
16: optional TConsistency consistency
17: optional i32 targetReplicaId
18: optional binary filterBytes
}
service THBaseService {
/**
* Get results for the provided TScan object.
* This helper function opens a scanner, get the results and close the scanner.
*
* @return between zero and numRows TResults
*/
list<TResult> getScannerResults(
/** the table to get the Scanner for */
1: required binary table,
/** the scan object to get a Scanner for */
2: required TScan tscan,
/** number of rows to return */
3: i32 numRows = 1
) throws (
1: TIOError io
)
}
雖然我沒用過thrift
, 但是看着這個數據包, 在看看這個數據結構的定義, 很明顯Field Id
就是定義文件中前面的數字啊.
很好, 這就可以定位到不用的字段爲: maxVersion
, 而這個字段指定了返回的版本數量, 0個版本自然就沒有數據咯.
解決
既然查詢到時因爲maxVersion
字段不同而導致的問題, 那麼在查詢的時候手動指定即可. 修改代碼後重試:
scan := &hbase.TScan{
maxVersion: 1
}
ret, err := client.GetScannerResults(context.Background(), []byte("test"), scan, 100)
if err != nil {
panic(err)
}
fmt.Println("scanner result")
fmt.Println(ret)
OK, 現在有數據了, 查半天沒想到是這個問題. 至此, 問題解決...
我是不懂爲什麼不同語言訪問還有這種差異? 是因爲字段的默認值不同? 搞不懂.
再順便提一句, 難道大家都沒有碰到這個問題麼? 我擱網上查了半天, 愣是沒找到一個沾邊的...若你也碰到了這個問題, 希望能夠幫到你