HBaseThrift使用以及Thriftserver分析

玄陵發表於2017-09-05

對於thriftserver 我們主要從2個大的方面進行分析:thrift的使用;thriftserver的部署;thriftserver的啟動,初始化;thriftserver的讀寫等請求處理;

一:thrift的使用
Thrift的主要目的是方便各個語言可以使用HBase,java,c++,py,PHP,等等;在我們下載下來的hbase的檔案裡面的下面的目錄:

hbase/hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift

你會看到2個資料夾,分別是:

hbase git:(hbase)  ll */*
-rw-r--r--  1 xxxxx xxxx    24K  8 15 09:40 thrift/Hbase.thrift
-rw-r--r--  1 xxxxx  xxxx    16K  8 15 09:40 thrift2/hbase.thrift

2個不同版本的Thrift檔案,有不同的區別,具體的區別參考連結的文章大概知道兩者的區別,這裡主要就一種的用法進行對應的介紹;

1>.首先要在自己的機器上面部署安裝thrift軟體;注意,服務端的thrift編譯環境和client需要一樣。我們雲HBase的Thrift的版本是0.9.0,所以希望你們編譯的thrift版本也是0.9.0,這樣以後進行互動才沒有問題;具體的安裝可以參考Thrift連結,也可以用別的方案,比如我是直接是用brew的命令列。

2>.thrift -gen php Hbase.thrift 會對應的在本地目錄下生產gen-py的對應的一個資料夾,如果是生產別的語言的話,使用別的命令,具體命令參考Thrift連結,當然這上面也有介紹thrift的安裝。

thrift git:(hbase)  ll
total 56
-rw-r--r--  1 xxxxx xxxx    24K  8 15 09:40 Hbase.thrift
drwxr-xr-x  4 xxxxx  xxxx   136B  8 23 16:02 gen-php

我們需要的是gen-php的資料夾,當然,也可以生產c++,py,java等等;

3>.實際上在Hbase.thrift的檔案裡面已經有告訴我們怎麼使用這些函式以及資料結構,不如我最簡單的現在需要進行get操作;使用的是php語言的話,那麼thrift文件裡面有下面的介紹;

  /**
   * Get a single TCell for the specified table, row, and column at the
   * latest timestamp. Returns an empty list if no such value exists.
   *
   * @return value for specified row/column
   */
  list<TCell> get(
    /** name of table */
    1:Text tableName,

    /** row key */
    2:Text row,

    /** column name */
    3:Text column,

    /** Get attributes */
    4:map<Text, Text> attributes
  ) throws (1:IOError io)
  
  /**
 * TCell - Used to transport a cell value (byte[]) and the timestamp it was
 * stored with together as a result for get and getRow methods. This promotes
 * the timestamp of a cell to a first-class value, making it easy to take
 * note of temporal data. Cell is used all the way from HStore up to HTable.
 */
struct TCell{
  1:Bytes value,
  2:i64 timestamp
}

上述的東西我們看到了,get方法,你需要人為的輸入table的name,row key,column 的name,以及你需要的些屬性,解釋的意思就是,get 一個單獨的cell,一列的資料;當然,如果你需要別的東西的話,那是另外的節後會有的;

4>.需要獲取得到thrift相關的依賴的庫,比如,Transport,Factory,Protocol等等,這個在thrift的最原始的包裡面會有的,但是還是要提醒下,一定要明確client 和服務端的thrift版本要一致,不然會出問題;

ini_set(`display_errors`, E_ALL);
$GLOBALS[`THRIFT_ROOT`] = `/root/hbase`;

require_once( $GLOBALS[`THRIFT_ROOT`] . `/Thrift.php` );
require_once( $GLOBALS[`THRIFT_ROOT`] . `/Thrift/transport/TSocket.php` );
require_once( $GLOBALS[`THRIFT_ROOT`] . `/Thrift/transport/TBufferedTransport.php` );
require_once( $GLOBALS[`THRIFT_ROOT`] . `/Thrift/protocol/TBinaryProtocol.php` );
require_once( $GLOBALS[`THRIFT_ROOT`] . `/gen-php/Hbase.php` );

$ip=xxxx;
$port=xx;
$socket = new TSocket($ip, $port);
$transport = new TBufferedTransport($socket);
$protocol = new TBinaryProtocol($transport);
$client = new HbaseClient($protocol);
$transport->open();
//case test


二:thriftserver的部署

關於hbase的thriftserver的部署,以及引數之類的這篇文章有介紹,這裡就不做介紹了。

上述文章中沒有對如果部署ThriftServer進行步驟性介紹,這裡介紹下,首先你下載了hbase-bin的二進位制包以後,你需要在本地搭建thriftservr的話,需要做3件事情:1.配置conf檔案,conf資料夾下的的hbase-site.xml檔案裡面需要配置下遠端的zk地址(遠端的hbase需要開啟本地的白名單);2.配置本地的thriftserver的堆記憶體,這個基於你的操作屬性進行設定,如果你的scan單次會涉及很多的資料,建議設定大等,不過一般都設定個128M就ok了,(ecs如果是記憶體足夠);3.到bin目錄下,執行sh hbase-daemon.sh start thrift 當然,這裡啟動哪個版本的thrift也是基於上面文章的介紹以及個人選擇進行的。然後就可以使用了。

四:Thriftserver的初始化啟動

最初的話,我們從最基本的啟動介面 開始入手看如何啟動Thriftserver,我們的啟動命令是sh hbas-deamon.sh start thrift/thrif2,啟動對應的thriftserver,我們可以進入最終啟動的bin目錄下的hbase的可執行檔案進行檢視,基於輸入的不同thrit 還是thrift2,啟動的是org.apache.hadoop.hbase.thrift.ThriftServer,org.apache.hadoop.hbase.thrift2.ThriftServer的這個class 檔案,這裡我們以簡單的org.apache.hadoop.hbase.thrift.ThriftServer進行對應的原始碼分析,分析啟動的時候做的操作,進入hbase的原始碼,以thriftserver的main方法進入:

 public static void main(String [] args) throws Exception {
    VersionInfo.logVersion();
    try {
      new ThriftServer(HBaseConfiguration.create()).doMain(args);
    } catch (ExitCodeException ex) {
      System.exit(ex.getExitCode());
    }
  }

以此為入口調到ThriftServerRunner內部的run方法,在這個函式內部我們會看到Thriftserver的初始化建立本地的server,包括設定使用與thrift的服務端相關資訊包括:TProtocolFactory,TProcessor,TTransportFactory等等基本資訊初始化完成,此外我們比較關心的就是這個thriftserver的server的初始化,這主要是基於不同的implType去建立,這裡預設使用的是THREAD_POOL 的方式的,我們以預設的方式往下看:

      // Thread pool server. Get the IP address to bind to.
      InetAddress listenAddress = getBindAddress(conf);
      int readTimeout = conf.getInt(THRIFT_SERVER_SOCKET_READ_TIMEOUT_KEY,
          THRIFT_SERVER_SOCKET_READ_TIMEOUT_DEFAULT);
      TServerTransport serverTransport = new TServerSocket(
          new TServerSocket.ServerSocketTransportArgs().
              bindAddr(new InetSocketAddress(listenAddress, listenPort)).
              backlog(backlog).
              clientTimeout(readTimeout));

      TBoundedThreadPoolServer.Args serverArgs =
          new TBoundedThreadPoolServer.Args(serverTransport, conf);
      serverArgs.processor(processor)
                .transportFactory(transportFactory)
                .protocolFactory(protocolFactory);
      LOG.info("starting " + ImplType.THREAD_POOL.simpleClassName() + " on "
          + listenAddress + ":" + Integer.toString(listenPort)
          + " with readTimeout " + readTimeout + "ms; " + serverArgs);
      TBoundedThreadPoolServer tserver =
          new TBoundedThreadPoolServer(serverArgs, metrics);
      this.tserver = tserver;
    

上述的程式碼,可以看到的就是,獲取得到繫結ip,預設是`0.0.0.0`,以及基於得到的埠(預設9090),設定的超時時間,設定好對應的TserverTransport,設定好相應的threadpoolserver的引數,然後設定好對應的初始化的server;這裡實際上這段程式碼以及對於thriftserver需要的ip port ,傳輸的thrift協議都設定好了(threadpoolserve內部也有對應的初始化的執行緒池),然後serve()方法內部會啟動對應的埠繫結以及初始化的執行緒池的操作,開始接受client的請求,可以理解的thriftserver的服務模型是,單執行緒的接收客戶端的請求,接收到請求,丟給後端的執行緒池進行操作;

上面大概介紹了預設的情況下,hbase的Thriftserver的啟動初始化,的相關操作,大概理順了一個流程,細節裡面的,各個操作的配置哪裡去取這些細小的地方,沒有描述;

三:thriftserver的呼叫

前段是thrift的處理,大概是從processer的process方法開始往下查,主要是從HBaseHandler進行觀測,比如get請求,落到這裡是get操作,我們看下面的程式碼:

  protected List<TCell> get(ByteBuffer tableName,
                              ByteBuffer row,
                              byte[] family,
                              byte[] qualifier,
                              Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
      Table table = null;
      try {
        table = getTable(tableName);
        Get get = new Get(getBytes(row));
        addAttributes(get, attributes);
        if (qualifier == null) {
          get.addFamily(family);
        } else {
          get.addColumn(family, qualifier);
        }
        Result result = table.get(get);
        return ThriftUtilities.cellFromHBase(result.rawCells());
      } catch (IOException e) {
        LOG.warn(e.getMessage(), e);
        throw getIOError(e);
      } finally {
        closeTable(table);
      }
    }

這裡往下看的話,實際上就和正常的java api的rpc請求一致了。其他的操作也是類似的。


相關文章