thrift 的原理和使用

Thrift 架構(gòu)?

Thrift是一個(gè)跨語(yǔ)言的服務(wù)部署框架,最初由Facebook于2007年開發(fā),2008年進(jìn)入Apache開源項(xiàng)目。Thrift通過(guò)IDL(Interface Definition Language,接口定義語(yǔ)言)來(lái)定義RPC(Remote Procedure Call,遠(yuǎn)程過(guò)程調(diào)用)的接口和數(shù)據(jù)類型,然后通過(guò)thrift編譯器生成不同語(yǔ)言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代碼負(fù)責(zé)RPC協(xié)議層和傳輸層的實(shí)現(xiàn)。

PS:CentOS下的Thrift的安裝流程可以參考這里


Thrift架構(gòu)


圖中,TProtocol(協(xié)議層),定義數(shù)據(jù)傳輸格式,例如:

TBinaryProtocol:二進(jìn)制格式;

TCompactProtocol:壓縮格式;

TJSONProtocol:JSON格式;

TSimpleJSONProtocol:提供JSON只寫協(xié)議, 生成的文件很容易通過(guò)腳本語(yǔ)言解析;

TDebugProtocol:使用易懂的可讀的文本格式,以便于debug

TTransport(傳輸層),定義數(shù)據(jù)傳輸方式,可以為TCP/IP傳輸,內(nèi)存共享或者文件共享等)被用作運(yùn)行時(shí)庫(kù)。

TSocket:阻塞式socker;

TFramedTransport:以frame為單位進(jìn)行傳輸,非阻塞式服務(wù)中使用;

TFileTransport:以文件形式進(jìn)行傳輸;

TMemoryTransport:將內(nèi)存用于I/O,java實(shí)現(xiàn)時(shí)內(nèi)部實(shí)際使用了簡(jiǎn)單的ByteArrayOutputStream;

TZlibTransport:使用zlib進(jìn)行壓縮, 與其他傳輸方式聯(lián)合使用,當(dāng)前無(wú)java實(shí)現(xiàn);


Thrift支持的服務(wù)模型

TSimpleServer:簡(jiǎn)單的單線程服務(wù)模型,常用于測(cè)試;

TThreadPoolServer:多線程服務(wù)模型,使用標(biāo)準(zhǔn)的阻塞式IO;

TNonblockingServer:多線程服務(wù)模型,使用非阻塞式IO(需使用TFramedTransport數(shù)據(jù)傳輸方式);


Thrift實(shí)際上是實(shí)現(xiàn)了C/S模式,通過(guò)代碼生成工具將thrift文生成服務(wù)器端和客戶端代碼(可以為不同語(yǔ)言),從而實(shí)現(xiàn)服務(wù)端和客戶端跨語(yǔ)言的支持。用戶在Thirft文件中聲明自己的服務(wù),這些服務(wù)經(jīng)過(guò)編譯后會(huì)生成相應(yīng)語(yǔ)言的代碼文件,然后客戶端調(diào)用服務(wù),服務(wù)器端提服務(wù)便可以了。


一般將服務(wù)放到一個(gè).thrift文件中,服務(wù)的編寫語(yǔ)法與C語(yǔ)言語(yǔ)法基本一致,在.thrift文件中有主要有以下幾個(gè)內(nèi)容:變量聲明(variable)、數(shù)據(jù)聲明(struct)和服務(wù)接口聲明(service, 可以繼承其他接口)。

下面分析Thrift的tutorial中帶的例子tutorial.thrift:

// 包含頭文件include “shared.thrift”? ? ? ? // 指定目標(biāo)語(yǔ)言namespace cpp tutorial? ? ? ? ? ? // 定義變量consti32 INT32CONSTANT =9853// 定義結(jié)構(gòu)體struct Work {

? 1: i32 num1 =0,

? 2: i32 num2,

? 3: Operation op,

? 4: optionalstring comment,

}// 定義服務(wù)service Calculator extends shared.SharedService {

/**

? * A method definition looks like C code. It has a return type, arguments,

? * and optionally a list of exceptions that it may throw. Note that argument

? * lists and exception lists are specified using the exact same syntax as

? * field lists in struct or exception definitions.

? */void ping(),

? i32 add(1:i32 num1,2:i32 num2),

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

? /**

? ? * This method has a oneway modifier. That means the client only makes

? ? * a request and does not listen for any response at all. Oneway methods

? ? * must be void.

? ? */? oneway void zip()

}



編譯thrift文件,生成C++代碼:

./thrift --gencpptutorial.thrift   #結(jié)果代碼存放在gen-cpp目錄下


如果是要生成java代碼:

./thrift --gen java tutorial.thrift  #結(jié)果代碼存放在gen-java目錄下


client端和sever端代碼要調(diào)用編譯.thrift生成的中間文件。

下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代碼




在client端,用戶自定義CalculatorClient類型的對(duì)象(用戶在.thrift文件中聲明的服務(wù)名稱是Calculator, 則生成的中間代碼中的主類為CalculatorClient), 該對(duì)象中封裝了各種服務(wù),可以直接調(diào)用(如client.ping()), 然后thrift會(huì)通過(guò)封裝的rpc調(diào)用server端同名的函數(shù)。

在server端,需要實(shí)現(xiàn)在.thrift文件中聲明的服務(wù)中的所有功能,以便處理client發(fā)過(guò)來(lái)的請(qǐng)求。



Thrift語(yǔ)法

Thrift文件支持shell命令,因此thrift是可執(zhí)行的。

Thrfit支持shell注釋風(fēng)格(#),也支持C/C++語(yǔ)言中單行(//)或者多行(/* */)注釋風(fēng)格


數(shù)據(jù)類型

1、基本類型

bool,布爾型,1個(gè)字節(jié);

byte,有符號(hào)單字節(jié);

i16,有符號(hào)16位整型;

i32,有符號(hào)32位整型;

i64,有符號(hào)64位整型;

double,64位浮點(diǎn)數(shù);

string,字符串;

binary,字節(jié)數(shù)組;

注意:thrift不支持無(wú)符號(hào)整型。


2、容器

map<t1,t2>,字典;

list<t1>,列表;

set<t1>,集合;

注意:容器中的元素類型可以是除了service 以外的任何合法thrift類型(包括結(jié)構(gòu)體和異常)。


3、結(jié)構(gòu)體 struct

Thrift結(jié)構(gòu)體在概念上同C語(yǔ)言結(jié)構(gòu)體類型—-一種將相關(guān)屬性聚集(封裝)在一起的方式;

在面向?qū)ο笳Z(yǔ)言中,thrift結(jié)構(gòu)體被轉(zhuǎn)換成類。

struct Work {

? 1: i32 num1 =0,

? 2: i32 num2,

? 3: Operation op,

? 4: optionalstring comment,

}

結(jié)構(gòu)體中,每個(gè)字段包含一個(gè)整數(shù)ID,數(shù)據(jù)類型、字段名,和一個(gè)可選的默認(rèn)值。

字段還可以聲明為"optional",當(dāng)該字段沒(méi)有設(shè)置的時(shí)候,不會(huì)被序列化輸出;

規(guī)范的struct定義中的每個(gè)域均會(huì)使用required或者optional關(guān)鍵字進(jìn)行標(biāo)識(shí)。如果required標(biāo)識(shí)的域沒(méi)有賦值,thrift將給予提示。如果optional標(biāo)識(shí)的域沒(méi)有賦值,該域?qū)⒉粫?huì)被序列化傳輸。如果某個(gè)optional標(biāo)識(shí)域有缺省值而用戶沒(méi)有重新賦值,則該域的值一直為缺省值。


4、異常?exception

異常在語(yǔ)法和功能上類似于結(jié)構(gòu)體,只不過(guò)異常使用關(guān)鍵字exception而不是struct關(guān)鍵字聲明。但它在語(yǔ)義上不同于結(jié)構(gòu)體,當(dāng)定義一個(gè)RPC服務(wù)時(shí),開發(fā)者可能需要聲明一個(gè)遠(yuǎn)程方法拋出一個(gè)異常。

exception InvalidOperation {

? 1: i32 what,

? 2:string why

}


5、服務(wù) service?

在流行的序列化/反序列化框架(如protocol buffer)中,Thrift是少有的提供多語(yǔ)言間RPC服務(wù)的框架。

Thrift編譯器會(huì)根據(jù)選擇的目標(biāo)語(yǔ)言為server產(chǎn)生服務(wù)接口代碼,為client產(chǎn)生樁代碼。

//“Twitter”與“{”之間需要有空格!!!service Twitter {

// 方法定義方式類似于C語(yǔ)言中的方式,它有一個(gè)返回值,一系列參數(shù)和可選的異常

// 列表. 注意,參數(shù)列表和異常列表定義方式與結(jié)構(gòu)體中域定義方式一致.voidping(),// 函數(shù)定義可以使用逗號(hào)或者分號(hào)標(biāo)識(shí)結(jié)束boolpostTweet(1:Tweet tweet);// 參數(shù)可以是基本類型或者結(jié)構(gòu)體,參數(shù)是只讀的(const),不可以作為返回值!!!

TweetSearchResult searchTweets(1:stringquery);// 返回值可以是基本類型或者結(jié)構(gòu)體

// ”oneway”標(biāo)識(shí)符表示client發(fā)出請(qǐng)求后不必等待回復(fù)(非阻塞)直接進(jìn)行下面的操作,

// ”oneway”方法的返回值必須是void

oneway voidzip()// 返回值可以是void

}

service中的函數(shù),其參數(shù)列表的定義方式與struct完全一樣;

service支持繼承,一個(gè)service可使用extends關(guān)鍵字繼承另一個(gè)service,struct不支持繼承;


6、枚舉類型 enum

enum TweetType {

TWEET,? ? ? ? // 編譯器默認(rèn)從1開始賦值RETWEET =2,// 可以賦予某個(gè)常量某個(gè)整數(shù)DM =0xa,//允許常量是十六進(jìn)制整數(shù)REPLY? ? ? ? // 末尾沒(méi)有逗號(hào)

}? ? ? ?

struct Tweet {1: required i32 userId;2: requiredstring userName;3: requiredstring text;4: optional Location loc;5: optional TweetType tweetType = TweetType.TWEET// 給常量賦缺省值時(shí),使用常量的全稱16: optionalstringlanguage ="english"}

注意:枚舉常量必須是32位的正整數(shù)


7、常量 const

Thrift允許用戶定義常量,復(fù)雜的類型和結(jié)構(gòu)體可使用JSON形式表示。

consti32 INT_CONST =1234;// 分號(hào)是可選的constmap MAP_CONST = {"hello":"world","goodnight":"moon"}


PS:跟C語(yǔ)言類似,Thrift也支持typedef語(yǔ)句,例如:

typedef i32 MyInteger


命名空間

Thrift中的命名空間同C++中的namespace類似,它們均提供了一種組織(隔離)代碼的方式。因?yàn)槊糠N語(yǔ)言均有自己的命名空間定義方式(如python中有module),thrift允許開發(fā)者針對(duì)特定語(yǔ)言定義namespace:

namespace cpp com.example.project namespacejava com.example.project



產(chǎn)生代碼

下面介紹Thrift產(chǎn)生各種目標(biāo)語(yǔ)言代碼的方式,


Thrift的網(wǎng)絡(luò)棧如下所示:


Transport層提供了一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)讀寫抽象層。這使得thrift底層的transport從系統(tǒng)其它部分(如:序列化/反序列化)解耦。

以下是一些Transport接口提供的方法:

open

close

read

write

listen

accept

flush


Protocol抽象層定義了一種將內(nèi)存中數(shù)據(jù)結(jié)構(gòu)映射成可傳輸格式的機(jī)制。換句話說(shuō),Protocol定義了datatype怎樣使用底層的Transport對(duì)自己進(jìn)行編解碼。因此,Protocol的實(shí)現(xiàn)要給出編碼機(jī)制并負(fù)責(zé)對(duì)數(shù)據(jù)進(jìn)行序列化。

Protocol接口的定義如下:

writeMessageBegin(name, type, seq)

writeMessageEnd()

writeStructBegin(name)

writeStructEnd()

writeFieldBegin(name, type, id)

writeFieldEnd()

writeFieldStop()

writeMapBegin(ktype, vtype, size)

writeMapEnd()

writeListBegin(etype, size)

writeListEnd()

writeSetBegin(etype, size)

writeSetEnd()

writeBool(bool)

writeByte(byte)

writeI16(i16)

writeI32(i32)

writeI64(i64)

writeDouble(double)

writeString(string)

name, type, seq = readMessageBegin()

readMessageEnd()

name = readStructBegin()

readStructEnd()

name, type, id = readFieldBegin()

readFieldEnd()

k, v, size = readMapBegin()

readMapEnd()

etype, size = readListBegin()

readListEnd()

etype, size = readSetBegin()

readSetEnd()bool= readBool()byte= readByte()

i16 = readI16()

i32 = readI32()

i64 = readI64()double= readDouble()string= readString()


Processor封裝了從輸入數(shù)據(jù)流中讀數(shù)據(jù)和向數(shù)據(jù)數(shù)據(jù)流中寫數(shù)據(jù)的操作。讀寫數(shù)據(jù)流用Protocol對(duì)象表示。Processor的結(jié)構(gòu)體非常簡(jiǎn)單:

interface TProcessor {

boolprocess(TProtocolin, TProtocolout) throws TException

}

與服務(wù)相關(guān)的processor實(shí)現(xiàn)由編譯器產(chǎn)生。Processor主要工作流程如下:從連接中讀取數(shù)據(jù)(使用輸入protocol),將處理授權(quán)給handler(由用戶實(shí)現(xiàn)),最后將結(jié)果寫到連接上(使用輸出protocol)。


Server將以上所有特性集成在一起:

(1) 創(chuàng)建一個(gè)transport對(duì)象

(2) 為transport對(duì)象創(chuàng)建輸入輸出protocol

(3) 基于輸入輸出protocol創(chuàng)建processor

(4) 等待連接請(qǐng)求并將之交給processor處理







參考文檔:

http://dongxicheng.org/search-engine/thrift-framework-intro/

http://dongxicheng.org/search-engine/thrift-guide/

http://dongxicheng.org/search-engine/thrift-internals/

http://dongxicheng.org/search-engine/thrift-bidirectional-async-rpc/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,477評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,617評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,807評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,049評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,432評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,769評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容