Thrift 入門教程【轉】

本文轉自本文轉載自:http://roclinux.cn/?p=3316

在英語裏,thrift是個名詞,表示的是“節儉、節約”,給個例句會印象更深些:

Farmers know a lot about value and thrift。(譯:農場主深諳價值與節儉之道。)

然而,這篇文章並非是一篇英語學習教程,而是要和大家介紹計算機技術裏一款著名的通信框架 – thrift框架。

好,現在有請thrift登場。

【thrift是什麼】

thrift的全名叫做Apache thrift,是一款軟件開發RPC框架,可以很高效地實現跨語言的RPC服務。

如果你還不瞭解RPC是什麼,趕快看看這裏

如果想參觀參觀thrift的官方網站,請點擊這裏

【thrift生於何地】

 

thrift最初生於Facebook,並茁壯成長,在2007年由Facebook正式開源出來,2008年由Apache軟件基金會開始負責項目孵化直至今日。

【還有哪些RPC框架】

protobuf、Avro、MessagePack等,如果你有興趣,可以搜索一下他們,也有利於你更好的瞭解RPC這個領域的發展情況。

【下載thrift】

目前的最新版本是thrift-0.9.1

【安裝thrift】

首先建議你安裝如下這些軟件包:

automake libtool flex bison pkgconfig gcc-c++ boost-devel \
libevent-devel zlib-devel python-devel ruby-devel

然後從官網下載thrift源碼包,並進行編譯鏈接:

./configure –-prefix=/your/program/path/ --enable-libtool-lock
make
make install

安裝完成後,你會看到thrift其實包含了三部分:一個bin程序、一坨頭文件和若干庫文件

#小知識 ls使用-F選項的話,文件列表中會用符號表示文件屬性,

#如*表示可執行文件,@表示軟連接,/表示文件夾

[rocrocket@li218-69 thrift]$ ls -1F bin
thrift*
[rocrocket@li218-69 thrift]$ ls -1F lib
libthrift-0.9.1.so*
libthrift.a
libthrift.la*
libthriftnb-0.9.1.so*
libthriftnb.a
libthriftnb.la*
libthriftnb.so@
libthrift.so@
libthriftz-0.9.1.so*
libthriftz.a
libthriftz.la*
libthriftz.so@
pkgconfig/

[rocrocket@li218-69 thrift]$ ls -1F include/thrift/

async/
concurrency/
config.h
cxxfunctional.h
processor/
protocol/
qt/
server/
TApplicationException.h
TDispatchProcessor.h
thrift-config.h
Thrift.h
TLogging.h
TProcessor.h
transport/
TReflectionLocal.h

【爲什麼需要thrift】

如果你之前沒有接觸過RPC框架的話,可能理解起來會比較困難。爲了照顧這些新用戶的感受,我嘗試着用一種好理解的思路來解釋:

研發工程師小吳接到了一個新任務,給“托福考試成績數據庫”增加一個“成績查詢”的功能,客戶端提供“用戶ID”向服務器端發起查詢請求,服務器端接到查詢請求後從數據庫中取回此用戶ID對應的姓名和成績,並返回給客戶端。

就是這樣一個簡單的Client-Server通信過程,其實就形成了一個典型的RPC場景。服務器端提供“成績查詢服務”,客戶端會通過約定的方法來查詢成績。

小吳設計的方法調用和數據傳輸是這樣的:

thrift example

thrift example

通過上圖可以看到,服務器端處於監聽狀態(等待請求的到來),客戶端發起一個名爲Search的動作,參數是用戶ID,而這個動作的返回是一個結構體struct UserGradeInfo,其中包含了用戶的名字(UserName)和用戶的成績(UserGrade)。

設計做完了,小吳要開始編碼了。如果按照“手工作坊”的思路,小吳至少需要完成如下幾個方面:

(1)“客戶端向服務器端發送數據”的代碼

(2)“客戶端接收服務器端查詢結果”的代碼

(3)“服務器端接收客戶端數據”的代碼

(4)“服務器端向客戶端發送查詢結果”的代碼

(5)如果客戶端會大批量發起查詢,那可能還需要考慮改成多線程模型或異步模型

(6)而且還有可能因爲某種原因,要求客戶端和服務器端使用不同的語言進行開發

照此思路,小吳至少要3周時間來開發和自測。(時間很長,對吧)

但,自從thrift出現後(更準確的說,是自從RPC開發框架出現後),上述工作量被大大簡化了。我們只要調用一個thrift工具就可以自動生成上述的所有代碼,即便服務器端和客戶端使用不同的語言,thrift也照樣支持。

至此,我相信你應該大概理解thrift能幫我們做什麼了吧。

【thrift到底怎麼用】

依然拿上面的“成績數據庫”的例子來說,thrift的使用可以被分爲四步:

第1步: 明確要交互的數據格式(如上例中的UserGradeInfo)和具體的方法(如上例中的Search),定義出thrift接口描述文件(英文叫做Inteface Description File);

第2步: 調用thrift工具,依據thrift接口文件,生成RPC代碼;

第3步: 你的服務器端程序引用thrift生成的RPC代碼,並實現其中的Search動作的邏輯,然後啓動監聽,等待客戶端發來請求。

第4步: 客戶端同樣引入並調用RPC代碼來與服務器端通信;

(如果你覺得這樣描述太空虛,別急,稍後會有完整的例子)

【thrift接口描述文件怎麼編寫】

如果你是學院派,那麼我推薦你研究thrift IDL(Interface Definition Language)規範,在這裏。雖然有些晦澀,但你可以從中瞭解到一個接口文件可以如何來寫。

在編寫接口文件時,需要對你要傳輸的數據設定數據類型,比如UserName是字符串型,UserGrade是整型等。因爲thrift是支持衆多開發語言的,所以thrift提供了一套自己的數據類型編寫規範,只有用這套獨立於任何語言的類型規範來編寫接口文件,thrift才能把它轉換成你指定的那種開發語言的代碼。

thrift中的類型包括基礎類型、結構、容器、異常、服務等幾個部分。(官網中有專門介紹數據類型的頁面,在這裏

【類型 之 基礎類型】

基礎類型,其實非常簡單和明確:

(1)bool:布爾類型(true或false)

(2)byte:8位有符號整數

(3)i16:16位有符號整數

(4)i32:32位有符號整數

(5)i64:64位有符號整數

(6)double:64位浮點數

(7)string:文本字符串,使用UTF-8編碼

(有些細心的同學會詢問“爲什麼不支持無符號整數類型呢?”,這是因爲在很多開發語言中並沒有原生的無符號整型。)

【類型 之 容器】

thrift容器包括了各種語言中最常用的容器,共三種:

(1)list容器:一個元素可重複的有序列表。會被轉換成C++中的vector,Java中的ArrayList,腳本語言中的數組等。

(2)set容器:一個元素不可重複的無序集合。會轉換成C++中的set,Java中的HashSet、Python中的Set等。(熟悉PHP的同學可能會問“PHP並不支持set類型,怎麼辦”,在PHP語言中,thrift會將set容器轉換成List。)

(3)map容器:一個含有多個key:value鍵值對的結構。會被轉換成C++中的map,Java中的HashMap,PHP中的關聯數組,Python/Ruby中的dictionary等。

對於上述三種容器,其元素的類型原則上可以是任何一種thrift類型。但是值得注意的是,map的key類型需要是基礎類型,因爲很多開發語言並不支持map的key類型爲複雜數據類型。

【類型 之 結構體】

結構體類型,在形式上和C/C++中的結構體類型非常相似,就是一坨類型的組合,比如上文圖中的UserGradeInfo便是一個thrift結構體類型。

thrift接口文件中的結構體類型,都會被轉換成一個獨立的類(Class)。類的屬性便是結構體中的各個類型,而類的方法便是對這些類型進行處理的相關函數。

我們來看一個結構體定義的例子:

struct UserGradeInfo {
1: required string UserName = "Anonymous";
2: required i16 UserGrade = 0;
}

可以看到,結構體中每一個域都有一個正整數標識符,這個標識符並不要求連續,但一旦定義,不建議再進行修改。

另外,每個域前都會有required或optional的限定,前者表示是必填域,後者則表示是可選域。域是可以有默認值的,比如上例中的“Anonymous”和0。

(1)如果一個域設置了required,但是在實際構造結構體時又沒有給這個域賦值,那麼thrift會認爲這是一個異常。

(2)如果一個域設置爲optional且在構造結構體時沒有給這個域賦值,那麼在使用這個結構體時,就會忽略掉這個optional的域。

【類型 之 異常】

除了使用exception來替代struct以外,“異常”這個類型,在語法上和剛纔介紹過的結構體的用法是完全一致的。但是從語義上講,exception和struct卻大相徑庭。exception是在遠程調用發生異常時用來拋出異常用的。

【類型 之 服務】

服務的定義,與面向對象技術中定義一個接口很類似,而這些接口其實就是純虛函數。thrift編譯工具會根據服務的定義來產生相應的方法和函數。

每個服務,都包括了若干個函數,每個函數包括了若干參數和一個返回值(返回值可以是void)。

(小技巧:返回值爲void的函數,你可以在函數名前加上oneway標識符,將此函數以異步模式執行,這樣在調用此函數後,函數會立即返回。)

對於返回void的函數,thrift仍然會確保函數返回,這樣就表示這個函數已被正確執行,且服務器端已有返回信息了。但是如果給void的函數前加上oneway,那麼此函數的返回只能表示數據已經進入傳輸層,並不能表示服務器端已經收到並返回了數據。

【我們來看一個thrift接口描述文件的例子吧】

# 例子 - thrift接口描述文件
#
# 編寫這個文件是爲了教會你如何寫thrift接口描述文件。
# 第一個你應該掌握的知識點就是.thrift文件
# 支持shell的註釋方式,那就是用#符號。
/**
* 我們首先來複習一下thrift的常用數據類型,如下所示:
*
* bool 布爾型,1個字節
* byte 有符號整數,1個字節
* i16 有符號16位整型
* i32 有符號32位整型
* i64 有符號64位整型
* double 64位浮點數值
* string 字符串類型
* binary 二進制數據類型(字節數組)
* list 單類型有序列表,允許有重複元素
* set 單類型無需集合,不允許有重複元素
* map<t1,t2> Map型(key:value)
*
  • 你發現了麼,.thrift文件還支持C語言的多行註釋形式。
    */

// 不賣關子了,其實我們還支持C語言的單行註釋形式呢 _

/**

  • .thrift文件可以引用其他.thrift文件,這樣就可以方便地把一些公共結構和服務囊括進來。
  • 在引用其他.thrift文件時,既可以直接引用當前文件夾下的文件,也可以引用其他路徑下的
  • 文件,但後者需要在thrift編譯工具編譯時加上-I選項來設定路徑。
  • 如果希望訪問被包含的.thrift文件中的內容,則需要使用.thrift文件的文件名作爲前綴,
  • 比如shared.SharedObject。我們在本例中引用了文件shared.thrift。
    */

include “shared.thrift”

/**

  • Thrift支持對.thrift文件中的類型設定namespace,這樣可以有效避免名字衝突。

  • 這種機制在C++中也叫做namespace,而在Java中叫做Package。

  • thrift支持針對不同的語言設置不同的namespace,比如下面的例子。

  • thrift會在生成不同語言代碼時,進行相應的設置。

*/

namespace cpp tutorial
namespace go tutorial
namespace java tutorial
namespace php tutorial
namespace perl tutorial
/**

  • thrift還可以使用typedef來給類型起別名。
    */
    typedef i32 MyInteger

/**

  • Thrift也支持定義常量。
  • 對於結構複雜的常量,支持使用JSON形式來表示。
    */
    const i32 MY_NUM = 9853
    const map<string,string> MY_MAP = {‘hello’:‘world’, ‘goodnight’:‘moon’}

/**

  • 你還可以定義枚舉類型, 其被指定爲32位整型。域的值是可以自定義的,而且
  • 當不提供域的值時,默認會從1開始編號並遞增。
    */

enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}

/**

  • 結構體則是一個複雜的數據類型。它由多個域組成,每個域會對應一個整數標識符,
  • 每一行的格式爲:一個冒號,一個類型,一個域名稱和一個(非必填的)默認值。
  • 每個域都可以設置爲optional或required來表示是否爲必填域,以便thrift決定是否
  • 在數據傳輸時要包含這個域。不指定時,默認爲required。
    */

struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
}

/**

  • 在語法上,異常的定義方式和結構體是完全一樣的。在發生問題時,可以拋出異常。
    */

exception InvalidOperation {
1: i32 what,
2: string why
}

/**

  • 啊哈,我們現在到了最Cool的環節,即定義服務。
  • (一個服務可以使用extends來繼承另一個服務。)
    */

service Calculator extends shared.SharedService {

/**

  • 服務中方法的定義非常類似於C語言的語法。它會包括一個返回值,
  • 一個參數列表以及一個可以拋出的異常列表(可選)
  • 可以提前告訴大家的是,定義參數列表的方法、定義異常列表的方法,
  • 和定義結構體的方法都是相似的,可以從下面的例子中看出。
  • 除了最後一個方法,其他的方法最後都要有一個逗號,大家可不要忽略這個細節。
    */

void ping(),
i32 add(1:i32 num1, 2:i32 num2),

/**

  • 在異常列表前,需要加throws關鍵字。
    */

i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

/**

  • 如下的這個方法有一個oneway修飾符,還記得他的作用麼
  • 這表示這個方法在調用後會立即返回,不會等待遠端的回覆。
  • 要注意的是,oneway只能修飾void返回類型。
  • oneway在英語裏就是“單向”的意思,還是很形象滴。
    */

oneway void zip()
}

/**

  • 在你使用thrift編譯工具編譯此文件後,
  • 會在當前目錄產生一個“gen-<你選擇的開發語言>”
  • 文件夾,比如你選擇的是C++語言,則會產生gen-cpp文件夾,
  • 裏面放着的便是thrift幫你生成好的代碼,
  • 代碼並不那麼晦澀,你可以打開看一看。
    */

【使用thrift編譯工具】

在我們編寫好thrift接口描述文件之後,thrift編譯工具就要派上用場了,它的作用就是根據thrift接口描述文件來生成相應開發語言的RPC代碼,以便用戶可以在自己的程序中調用。

thrift編譯工具的名稱就是thrift,其最常見的使用方式是這樣的:

thrift --gen ${開發語言} ${thrift接口描述文件}

# 運行了上述命令之後,就會在當前文件夾下生成一個以“gen-${開發語言}”命名的文件夾,
# 裏面便是自動生成的代碼。

【thrift會自動生成哪些代碼呢】

在編譯之後,thrift會生成這些文件:(我們以mytime.thrift爲例)

$ cd gen-cpp

$ ls -1 mytime_types.*

mytime_types.cpp
mytime_types.h

# 和數據類型有關的內容,會命名爲

# thriftfilenametypes.h/{thrift_file_name}_types.h/{thrift_file_name}_types.cpp。
# 比如你使用typedef定義了類型別名,
# 或者你定義了一個struct類型,都會在這兩個文件中記錄。

# 對於struct類型,有必要多說一下,thrift會針對每一個struct類型

# 生成一個對應的類,類中會包括一個構造函數、一個析構函數、

# 域變量定義、用於設置域值的__set_XXX()方法、

# 重載比較符(==,!=,<)、設定讀寫此結構體的方法read/write,

# 以及一個用於表示域是否設置了值的_${struct name}__isset結構體。

# 另外,還會有一個獨立定義的swap方法用來進行兩個結構體的值交換。

$ ls -1 mytime_constants.*
mytime_constants.cpp
mytime_constants.h

# 和常量有關的內容,會命名爲
# thriftfilenameconstants.h/{thrift_file_name}_constants.h/{thrift_file_name}_constants.cpp。
# 在.h頭文件中會有一個${thrift_file_name}Constants類,
# 其中會包括一個顯式的構造函數,以及常量的域。
# 而在cpp文件中則會在相應的構造函數中對這個常量進行賦值。

$ ls -1 myservice*
myservice.cpp
myservice.h
myservice_server.skeleton.cpp

# 針對每一個service會產生一套對應的文件,命名爲
# servicename.cpp/{service_name}.cpp/{service_name}.h/KaTeX parse error: Expected 'EOF', got '#' at position 71: …"hljs-comment">#̲ 在{service_name}.h中會看到有若干個類,他們都是以服務名作爲前綴的,

# 其中包括servicenameIf{service_name}If、{service_name}IfFactory、
# servicenameIfSingletonFactory{service_name}IfSingletonFactory、{service_name}Null、
# servicenameClient{service_name}Client、{service_name}Processor、
# servicenameProcessorFactory{service_name}ProcessorFactory、{service_name}Multiface。

# 另外,thrift還會針對服務中的每一個具體的方法分別產生四個對應的類,即
# KaTeX parse error: Expected group after '_' at position 15: {service_name}_̲{method}_args、KaTeX parse error: Expected group after '_' at position 15: {service_name}_̲{method}_pargs、
# KaTeX parse error: Expected group after '_' at position 15: {service_name}_̲{method}_result、KaTeX parse error: Expected group after '_' at position 15: {service_name}_̲{method}_presult。

# 另外,${service_name}_server.skeleton.cpp是一個server的模板例子。

【一起用thrift來做個項目!】

從我的學習經驗來看,框架的學習路線是“瞭解應用場景 -> 瞭解用法 -> 看例子 -> 深入用戶 -> 自己寫例子”。我相信,如果你能和我一起走完這個例子,一定會消除對thrift的恐懼,愛上這款RPC框架的。

我們的例子很簡單,就是一個“時間問答”機器人,英文叫做WhatTime,客戶會向服務器端詢問現在幾點啦,服務器端會把現在的時間回答給客戶端。就像這樣:

客戶端:請問,現在幾點啦?

服務器端:現在是上午10點01分。

我們會在服務器端使用C++來實現,而在客戶端會使用C++語言來實現一版,還會使用最近很流行的Go語言實現一版。(Go語言可是未來可能撼動IT界的語言之一哦)

thrift接口描述文件WhatTime.thrift:

namespace cpp roctime

service TimeService {

i32 TellMeTime()

}

需要經過thrift編譯工具編譯:

$ thrift --gen cpp WhatTime.thrift

$ ls -1F

gen-cpp/
WhatTime.thrift

$ cd gen-cpp/
$ ls -1

TimeService.cpp
TimeService.h
TimeService_server.skeleton.cpp
WhatTime_constants.cpp
WhatTime_constants.h
WhatTime_types.cpp
WhatTime_types.h

然後,我們把server的樣例文件重命名一下:

$ mv TimeService_server.skeleton.cpp server.cpp

我們將server.cpp中的TellMeTime方法做一些修改,加入報告時間的邏輯:

int32_t TellMeTime() {
// Your implementation goes here
time_t now_time = time(NULL);
return now_time;

}

好了,server.cpp完工,我們對server.cpp進行編譯鏈接:

g++ -I /home/roc/program/thrift/include -c TimeService.cpp
g++ -I /home/roc/program/thrift/include -c WhatTime_constants.cpp
g++ -I /home/roc/program/thrift/include -c WhatTime_types.cpp
g++ -I /home/roc/program/thrift/include -c server.cpp
g++ -L /home/roc/program/thrift/lib/ TimeService.o WhatTime_constants.o WhatTime_types.o server.o -o server -lthrift

如果提示找不到thrift動態鏈接庫,那就需要把thrift的lib路徑(如/home/roc/program/thrift/lib)加入到ld.so.conf中,然後執行ldconfig命令在重新將動態鏈接庫裝載到cache中。

然後就可以直接運行./server了,可以看到9090端口打開,已經開始服務了。

下面,我們繼續編寫客戶端的代碼。thrift並沒有給出客戶端的代碼樣例,所以需要自己來開發。

#include "TimeService.h"
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>

#include <iostream>
using namespace std;

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace roctime;

int main(int argc, char *argv[]) {

boost::shared_ptr<TSocket>socket(new TSocket(“localhost”, 9090));
boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

time_t mytime = 0;

TimeServiceClient client(protocol);

transport->open();

mytime = client.TellMeTime();

cout << "Now is " << ctime(&amp;mytime) << endl;

transport->close();

return 0;

}

開發完成後,我們對client進行編譯鏈接:

g++ -I /home/roc/program/thrift/include -c TimeService.cpp
g++ -I /home/roc/program/thrift/include -c WhatTime_constants.cpp
g++ -I /home/roc/program/thrift/include -c WhatTime_types.cpp
g++ -I /home/roc/program/thrift/include -c client.cpp
g++ -L /home/roc/program/thrift/lib/ TimeService.o WhatTime_constants.o WhatTime_types.o client.o -o client -lthrift

好了,服務器端程序server和客戶端程序client都生成好了,可以試着運行這個例子:

在一個終端運行服務器端程序:

$ ./server

在另一個終端打開客戶端程序:

$ ./client
Now is Fri Nov 1 12:14:06 2013

順利的話,你應該可以看到運行server的終端窗口會輸出“Now is Fri Nov 1 12:14:06 2013”啦!RPC通信成功了!

至此,C++版本的客戶端和服務器端都已經實現了。是不是並沒有那麼的難呢!下面,我們來看看Go語言的客戶端如何實現。

【Go語言版客戶端】

首先通過thrift來生成go的代碼:

$ thrift -gen go WhatTime.thrift

會生成gen-go文件夾,進入其中,可以看到這裏面有什麼東東:

$ cd gen-go/
$ ls -1F
WhatTime/

在gen-go文件夾中,我們作如下的操作:

#有人會問爲什麼要搞個src文件夾呢,稍後你就會知道答案
$ mkdir src

#將thrift自動生成的WhatTime文件夾移動到src中
$ mv WhatTime src/

#將當初源碼安裝thrift時的文件夾(也就是tar.gz解包後的文件夾)中的lib/go/thrift拷貝到src中。
#拷貝過來的thrift文件夾中全都是.go文件,這些便是thrift支持go語言的庫文件,用於我們稍後編譯鏈接所用。

$ cp -r /path/to/source/thrift/lib/go/thrift/ .

#都完成後,我們看看gen-go/src文件夾中的目錄結構

$ ls -1F
thrift/
WhatTime/

#設置GOPATH全局變量,以便Go語言能查找到所需的包
#當我們設置了GOPATH之後,Go語言會默認在KaTeX parse error: Expected 'EOF', got '#' at position 21: …H/src下來查找相應的包, #̲這下你應該明白爲什麼當初要創建…GOPATH:/path/to/gen-go"

#除此之外,你還要確保Go語言已經正確安裝,且PATH和GOROOT也已正確設置。

export PATH="/path/to/go_dir/bin:${PATH}"
export GOROOT=""/path/to/go_dir"

下一步,我們需要對$GOPATH/src/WhatTime中的constants.go、time_service.go和ttypes.go三個文件做一下小的修改:

我們將import區域中的
"git.apache.org/thrift.git/lib/go/thrift"
修改爲
"thrift"

作如上修改的原因,其一是我們已經在本地準備好了支持thrift的go語言包,
其二是因爲這樣可以避免在無法連接到互聯網的情況下,程序編譯失效。

說實話,Go語言的準備工作確實有些繁瑣,希望你還有耐心看最關鍵的內容,那就是編寫client.go的代碼!

我們在src的同級目錄中來編寫,client.go的代碼如下:

package main

import (
“WhatTime”
“fmt”
“thrift”
“time”
)

func handleClient(client *WhatTime.TimeServiceClient) (err error) {

t, _ := client.TellMeTime()

fmt.Println(time.Unix(<span class="hljs-keyword">int64</span>(t), <span class="hljs-number">0</span>).String())

<span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>

}

func runClient(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string) error {

<span class="hljs-keyword">var</span> transport thrift.TTransport

transport, err := thrift.NewTSocket(addr)

<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
	fmt.Println(<span class="hljs-string">"Error opening socket:"</span>, err)
	<span class="hljs-keyword">return</span> err
}

transport = transportFactory.GetTransport(transport)

<span class="hljs-keyword">defer</span> transport.Close()
<span class="hljs-keyword">if</span> err := transport.Open(); err != <span class="hljs-literal">nil</span> {
	<span class="hljs-keyword">return</span> err
}

<span class="hljs-keyword">return</span> handleClient(WhatTime.NewTimeServiceClientFactory(transport, protocolFactory))

}

func main() {

<span class="hljs-keyword">var</span> protocolFactory thrift.TProtocolFactory

protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()

<span class="hljs-keyword">var</span> transportFactory thrift.TTransportFactory

transportFactory = thrift.NewTBufferedTransportFactory(<span class="hljs-number">1024</span>)

addr := <span class="hljs-string">"localhost:9090"</span>

<span class="hljs-keyword">if</span> err := runClient(transportFactory, protocolFactory, addr); err != <span class="hljs-literal">nil</span> {

	fmt.Println(<span class="hljs-string">"error running client:"</span>, err)

}

}

然後進行編譯鏈接和運行:

$ go build client.go
$ ./client
2013-11-01 12:37:31 +0800 CST


【結語】至此,我們的Go語言版本也大功告成了!

如果你耐心地看到了這裏,說明你完成了thrift的入門。但是“紙上得來終覺淺,自己動手纔是真”。

後面還會有Thrift進階篇,敬請期待。

如果你覺得這篇文章對你有一些幫助,希望能通過捐款的方式支持Linux大棚和博主:)

謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章