php通過 thrift訪問hadoop的hive

來源:互聯網
上載者:User

本文講解php通過sql查詢hadoop中的資料。主要使用的技術是:php通過thrift向hive提交sql查詢,hive將sql查詢轉換成hadoop任務,等到hadoop執行完畢後,返回一個結果uri,然後我們只需要讀取這個uri中的內容。

Thrift的誕生是為瞭解決不同語言之間的訪問的問題,可以支援多種程式語言,如c++、php、java、python等。Thrift是由facebook開發的輕量級跨語言的服務架構,現在已經移交到apache基金會下。和它類似的有google出的protocol buffer和ice。Thrift的一大優勢是支援的語言很豐富,它使用自己的IDL語言來服務介面和資料交換的格式。

Hive是可以使用類似sql的語言訪問HBase。而HBase是一個開源的nosql產品,它實現了google bigtable論文的一個開源產品,和hadoop、hdfs一起,可以用來儲存和處理海量column family資料。

Thrift的官方網址:http://thrift.apache.org/

一.伺服器端下載安裝thrift

在hadoop和hbase都已經安裝好的叢集上安裝thrift。
(1)下載:wget http://mirror.bjtu.edu.cn/apache//thrift/0.8.0/thrift-0.8.0.tar.gz,下載thrift用戶端源碼包。

(2)解壓:tar -xzf thrift-0.8.0.tar.gz
(3)編譯安裝:如果是源碼編譯的,首先需要執行./bootstrap.sh建立./configure檔案;
接下來執行./configure;
再執行make && make install
(4)啟動:./bin/hbase-daemon.sh start thrift
Thrift預設監聽的連接埠是9090。
參考連結:http://blog.csdn.net/hguisu/article/details/7298456

二.建立.thrift檔案

Thrift編譯器安裝好之後,需要建立一個thrift檔案。該檔案為一個介面定義檔案,需要定義thrift的類型(types)和服務(services)。該檔案中定義的服務(services)是由伺服器端實現的,並由用戶端進行調用。Thrift編譯器的作用是將你寫的thrift檔案產生為用戶端介面源碼,該介面源碼是由不同的用戶端庫和你寫的服務來產生的。為了通過thrift檔案產生不同語言的介面源碼,我們需要運行:

thrift --gen <language> <Thrift filename>

三.thrift檔案描述

支援的變數類型

類型           描述    bool            #true, false    byte            #8位的有符號整數    i16             #16位的有符號整數    i32             #32位的有符號整數    i64             #64位的有符號整數    double          #64位的浮點數    string          #UTF-8編碼的字串    binary          #字元數組    struct          #結構體    list<type>        #有序的元素列表,類似於STL的vector    set<type>     #無序的不重複元素集,類似於STL的set    map<type1,type2>  #key-value型的映射,類似於STL的map    exception       #是一個繼承於本地語言的exception基類    service         #服務包含多個函數介面(純虛函數)
四.從服務端到用戶端如何開發1.簡單的helloworld程式

這裡使用python做服務端,php做用戶端,當然也可以使用c++來做服務端。下面開始介紹開發流程。

(1)首先我們在伺服器端寫個helloworld.thrift檔案,如下所示:

service HelloWorld{     string ping(1: string name),     string getpng(),  }

(2)在伺服器端編譯helloworld.thrift

編譯helloworld.thrift檔案,會產生伺服器端和用戶端相應語言的介面源碼。
/usr/local/thrift/bin/thrift -r --gen py helloworld.thrift  
/usr/local/thrift/bin/thrift -r --gen php helloworld.thrift  
#會在目前的目錄下產生 gen-* 目錄。
產生的gen-py目錄放在伺服器上,產生的gen-php目錄放在用戶端上。
(3)編寫伺服器端代碼

    import sys      sys.path.append('./gen-py')             from helloworld import HelloWorld      from helloworld.ttypes import *             from thrift.transport import TSocket      from thrift.transport import TTransport      from thrift.protocol import TBinaryProtocol      from thrift.server import TServer             class HellowordHandler:          def __init__ (self):              pass                 def ping (self, name):              print name + ' from server.'              return "%s from server." % name          def getpng (self):              f = open("./logo.png", "rb")              c = f.read()              f.close()              return c      handler = HellowordHandler()      processor = HelloWorld.Processor(handler)      transport = TSocket.TServerSocket(9090)      tfactory = TTransport.TBufferedTransportFactory()      pfactory = TBinaryProtocol.TBinaryProtocolFactory()             server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)             # You could do one of these for a multithreaded server      #server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)      #server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)             print 'Starting the server...'      server.serve()      print 'done.'  

(4)編寫用戶端代碼

先將gen-php目錄拷貝到用戶端上。

<?php  try{      //包含thrift用戶端庫檔案      $GLOBALS['THRIFT_ROOT'] = './php/src';       require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';      require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';      require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';      require_once $GLOBALS['THRIFT_ROOT'].'/transport/THttpClient.php';      require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';      error_reporting(E_NONE);      //包含helloworld介面檔案    $GEN_DIR = './gen-php';      require_once $GEN_DIR.'/helloworld/HelloWorld.php';      error_reporting(E_ALL);         $socket = new TSocket('*.*.*.*', 9090);      $transport = new TBufferedTransport($socket, 1024, 1024);      $protocol = new TBinaryProtocol($transport);      $client = new HelloWorldClient($protocol);          $transport->open();         $a = $client->ping('xyq ');      echo $a;         $transport->close();         }catch(TException $tx){          print 'TException: '.$tx->getMessage()."/n";      }     ?>

最後給出一篇參考連結:http://blog.csdn.net/heiyeshuwu/article/details/5982222

2.thrift官網上給出的例子

Apache thrift能夠讓你在一個簡單的.thrift檔案中,定義資料類型和服務介面。將該.thrift檔案作為輸入檔案,通過編譯器編譯產生服務端和用戶端源碼,從而構建了RPC用戶端和伺服器端之間的跨語言編程。
下面直接給出關鍵代碼。

(1)thrift定義檔案

/*定義的介面資料類型*/struct UserProfile {        1: i32 uid,        2: string name,        3: string blurb}/*定義的介面函數*/service UserStorage {        void store(1: UserProfile user),        UserProfile retrieve(1: i32 uid)}

(2)用戶端python實現

  # Make an object  up = UserProfile(uid=1,      name="Test User",      blurb="Thrift is great")  # Talk to a server via TCP sockets, using a binary protocol  transport = TSocket.TSocket("localhost", 9090)  transport.open()  protocol = TBinaryProtocol.TBinaryProtocol(transport)  # Use the service we already defined  service = UserStorage.Client(protocol)  service.store(up)  # Retrieve something as well  up2 = service.retrieve(2)

(3)伺服器端c++實現

class UserStorageHandler : virtual public UserStorageIf {   public:    UserStorageHandler() {      // Your initialization goes here    }    void store(const UserProfile& user) {      // Your implementation goes here      printf("store\n");    }    void retrieve(UserProfile& _return, const int32_t uid) {      // Your implementation goes here      printf("retrieve\n");    }  };  int main(int argc, char **argv) {    int port = 9090;    shared_ptr<UserStorageHandler> handler(new UserStorageHandler());    shared_ptr<TProcessor> processor(new UserStorageProcessor(handler));    shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));    shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());    shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());    TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);    server.serve();}
3.實戰經曆

(1).thrift介面檔案

檔案名稱為hive.thrift,如下所示:

namespace java com.gj.data.hive.thrift/** * 向HADOOP提交HIVE任務類。典型的用法是提交任務,輪詢任務是否完成,取得任務的結果URI,讀取結果檔案。 *這裡展示的是用戶端為java語言時的用法。 *           long taskId = client.submitTask("abc@gj.com", "web", "select * from table1 where dt = '2013-04-10' limit 10;"); *           if (taskId <=0) { *               System.out.println("error submit"); *               return; *           }             //輪詢任務是否完成 *           int count = 50; *           while(count >= 0) { *               try { *                   Thread.sleep(30 * 1000); *               } catch (InterruptedException ex) {} *               if (client.isTaskFinished(taskId)) { *                   System.out.println(client.getResultURI(taskId)); *                   break; *               }  *               count --; *          } */service Hive {    /** 提交任務     * user - 使用者名稱,工作郵箱,如abc@xxx.com     * env - 提交的環境。目前支援兩個環境: mobile - 移動端, web - 主站。     * sql - 提交的sql語句。     * 傳回值:成功提交返回大於0的任務id值,此id用於後續查詢。失敗返回0或-1.     */    i64 submitTask(1:string user, 2:string env, 3:string sql);        /** 查看任務是否完成     *  taskId - 任務號     * 傳回值:完成返回true,未完成返回false     */    bool isTaskFinished(1:i64 taskId);        /** 取得任務結果的URI,可以用此URI獲得結果資料     *  taskId - 任務號     * 傳回值:任務有結果,返回URI,否則返回Null 字元串     */        string getResultURI(1:i64 taskId);        /** 取得使用者的所有工作清單     *  user - 使用者名稱,完整的ganji郵箱     * 傳回值:任務號列表,如果沒有任務,返回空     */    list<i64> getTasksByUserName(1:string user);}

(2).產生php與hbase的介面檔案(伺服器端實現)

/usr/local/thrift/bin/thrift --gen php hive.thrift
然後會在gen-php目錄下產生了Hive.php和Hive_types.php檔案。
然後把Hive.php和Hive_types.php檔案拷貝到:用戶端php開發的目錄下。

(3).配置php用戶端

php作為用戶端使用thrift時,需要進行如下配置。

(a)準備thrift php用戶端基礎類

這些基礎類可以從thrift的源碼包中找到。在thriftsrc/lib/php/src下,一班有如下目錄和檔案:ext/,protocol/,transport/目錄,和thrift.php、autoload.php檔案。我們把這些目錄和檔案拷貝到用戶端的/server/www/thrift_part/thrift-0.5.0/目錄下。

(b)配置php的thrift擴充,使其支援thrift

如果php想要使用thrift,還需要安裝php的thrift擴充。
如下所示:
首先下載相應的php的thrift擴充,進行解壓;
進入源碼下的ext/thrift_protocol;
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config --enable-thrift_protocol
make
make install
然後把產生的thrift_protocol.so檔案配置到php.ini並重啟apache服務。

(4).php用戶端的實現

檔案名稱為:updateHiveData.php

<?php    $GLOBALS['THRIFT_ROOT'] = '/server/www/third_part/thrift-0.5.0';        require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';    require_once $GLOBALS['THRIFT_ROOT'].'/packages/scribe/scribe.php';    require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';    require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';    require_once $GLOBALS['THRIFT_ROOT'].'/transport/THttpClient.php';    require_once $GLOBALS['THRIFT_ROOT'].'/transport/TFramedTransport.php';    require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';    //產生的檔案    require_once dirname(__FILE__) . '/Hive.php';    //require_once dirname(__FILE__) .'/hive_types.php';        ERROR_REPORTING(E_ALL);    INI_SET('DISPLAY_ERRORS','ON');       $socket = new TSocket('hive.corp.gj.com',13080);    $socket->setDebug(TRUE);         // 設定接收逾時(毫秒)    $socket->setSendTimeout(10000);    $socket->setRecvTimeout(10000);        //$transport = new TBufferedTransport($socket, 1024, 1024);    $transport = new TFramedTransport($socket);    $protocol = new TBinaryProtocol($transport);    $client = new HiveClient($protocol);        try{        $transport->open();    }catch(TException $tx){        echo $tx->getMessage();    }         //擷取各個類目某一天的 PV UV    $taskId = $client->submitTask('xxx@xxx.com','web',"select regexp_extract(gjch, '^/([^/]+)', 1), count(*), count(distinct uuid) from table1 where dt = '2013-04-22' and gjch regexp '[^@]*/detail' GROUP BY regexp_extract(gjch, '^/([^/]+)', 1);");        if($taskId <= 0){echo 'error submit';        exit;    }    echo $taskId . "\n";       $count = 50;    while($count > 0){        try{            //sleep以秒為單位,這裡3分鐘輪詢一次            sleep(3*60);        }catch(TException $tx){}if($client->isTaskFinished($taskId)){            //echo $client->getResultURI($taskId);            $url = $client->getResultURI($taskId);            //echo $url;        $handle = fopen($url,"rb");            $content = stream_get_contents($handle);            echo $content;            fclose($handle);            break;        }        $count--;    }      $transport->close();?>

由於伺服器端不是本人負責,所以當時只根據thrift定義檔案,實現了php用戶端。運行結果如下:

其中這裡url是$client->getResultURI取得的結果,網頁內容是這個uri對應的內容。

五.thrift類說明

TSocket:採用TCP socket進行資料轉送;
Transport類(傳輸層):
負責資料轉送,介紹幾個常用類:
TBufferedTransport:對某個Transport對象操作的資料進行buffer,即從buffer中讀取資料進行傳輸,或者將資料直接寫入buffer;
TFramedTransport:同TBufferdTransport類型,也會對資料進行buffer,同時它支援定長資料發送和接受;
TFileTransport:檔案(日誌)傳輸類,允許client將檔案傳給server,語序server將受到的資料寫到檔案中;

Protocol類(協議):
負責資料編碼,主要有以下幾個常用類:
TBinaryProtocol:二進位編碼;
TJSONProtocol:JSON編碼。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.