• hbase/thrift/go连接失败


    问题

    在通过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方法是可以获取到数据的, 那么, 就是说仅GoGetScannerResults方法是有问题的???

    当问题追溯到这里的时候, 我已经有些懵了, 尝试这谷歌查找原因, 各种关键词都试过了, 没有找到想要的答案(这也是为什么我要把这个错记下来, 因为没查到啊)

    抓包比较差异

    找了半天没有找到问题, 既然Python可以查到数据 而Go查不到, 那么, 就可以尝试抓包比较两者差异了嘛.

    依然使用上面的GetScannerResults, 分别对pythongo进行抓包分析, 通过命令tmpdump port 9091 -w thrift.cap:

    image-20220417134443514

    image-20220417134458800

    可以看到, 其中的thrift请求也能够被wireshark识别了, 那就太好了, 不用比较二进制了, 来看一下两个请求的区别:

    Python请求如下:

    image-20220417134751836

    Go请求如下:

    image-20220417134903197

    其中, 有一个字段两个是明显不一样的, 我很贴心的用红框圈出来了, 那么, 问题来了, 这个字段是什么呢? 我把上面的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, 现在有数据了, 查半天没想到是这个问题. 至此, 问题解决...

    我是不懂为什么不同语言访问还有这种差异? 是因为字段的默认值不同? 搞不懂.

    再顺便提一句, 难道大家都没有碰到这个问题么? 我搁网上查了半天, 愣是没找到一个沾边的...若你也碰到了这个问题, 希望能够帮到你

    原文地址: https://hujingnb.com/archives/767

  • 相关阅读:
    查看某个存储过程
    qemu-libvirt-kvm三者之间的关系
    gitlab安装
    jenkins安装
    数据库迁移(分享十一续集)
    数据库迁移(分享十一续集)
    数据库迁移(分享十一续集)
    数据库迁移(分享十一)
    云上迁移(分享十)
    阿里云迁移(分享九)
  • 原文地址:https://www.cnblogs.com/hujingnb/p/16155637.html
Copyright © 2020-2023  润新知