thrift 的原理和使用

thrift 的原理和使用

Thrift 架構 

Thrift是一個跨語言的服務部署框架,最初由Facebook於2007年開發,2008年進入Apache開源項目。Thrift經過IDL(Interface Definition Language,接口定義語言)來定義RPC(Remote Procedure Call,遠程過程調用)的接口和數據類型,而後經過thrift編譯器生成不一樣語言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),並由生成的代碼負責RPC協議層和傳輸層的實現。html

PS:CentOS下的Thrift的安裝流程能夠參考這裏java

 

Thrift架構python

 

圖中,TProtocol(協議層),定義數據傳輸格式,例如:shell

  • TBinaryProtocol:二進制格式;
  • TCompactProtocol:壓縮格式;
  • TJSONProtocol:JSON格式;
  • TSimpleJSONProtocol:提供JSON只寫協議, 生成的文件很容易經過腳本語言解析;
  • TDebugProtocol:使用易懂的可讀的文本格式,以便於debug

TTransport(傳輸層),定義數據傳輸方式,能夠爲TCP/IP傳輸,內存共享或者文件共享等)被用做運行時庫。apache

  • TSocket:阻塞式socker;
  • TFramedTransport:以frame爲單位進行傳輸,非阻塞式服務中使用;
  • TFileTransport:以文件形式進行傳輸;
  • TMemoryTransport:將內存用於I/O,java實現時內部實際使用了簡單的ByteArrayOutputStream;
  • TZlibTransport:使用zlib進行壓縮, 與其餘傳輸方式聯合使用,當前無java實現;

 

Thrift支持的服務模型centos

  • TSimpleServer:簡單的單線程服務模型,經常使用於測試;
  • TThreadPoolServer:多線程服務模型,使用標準的阻塞式IO;
  • TNonblockingServer:多線程服務模型,使用非阻塞式IO(需使用TFramedTransport數據傳輸方式);

 

Thrift其實是實現了C/S模式,經過代碼生成工具將thrift文生成服務器端和客戶端代碼(能夠爲不一樣語言),從而實現服務端和客戶端跨語言的支持。用戶在Thirft文件中聲明本身的服務,這些服務通過編譯後會生成相應語言的代碼文件,而後客戶端調用服務,服務器端提服務即可以了。數組

 

通常將服務放到一個.thrift文件中,服務的編寫語法與C語言語法基本一致,在.thrift文件中有主要有如下幾個內容:變量聲明(variable)、數據聲明(struct)和服務接口聲明(service, 能夠繼承其餘接口)。服務器

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

複製代碼
// 包含頭文件
include 「shared.thrift」        

// 指定目標語言
namespace cpp tutorial            

// 定義變量
const i32 INT32CONSTANT = 9853        

// 定義結構體
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

// 定義服務
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 --gen cpp tutorial.thrift   #結果代碼存放在gen-cpp目錄下

 

若是是要生成java代碼:

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

 

client端和sever端代碼要調用編譯.thrift生成的中間文件。
下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代碼

 

 

 

在client端,用戶自定義CalculatorClient類型的對象(用戶在.thrift文件中聲明的服務名稱是Calculator, 則生成的中間代碼中的主類爲CalculatorClient), 該對象中封裝了各類服務,能夠直接調用(如client.ping()), 而後thrift會經過封裝的rpc調用server端同名的函數。
在server端,須要實如今.thrift文件中聲明的服務中的全部功能,以便處理client發過來的請求。

 


 

Thrift語法

Thrift文件支持shell命令,所以thrift是可執行的。

Thrfit支持shell註釋風格(#),也支持C/C++語言中單行(//)或者多行(/* */)註釋風格

 

數據類型

一、基本類型

  • bool,布爾型,1個字節;
  • byte,有符號單字節;
  • i16,有符號16位整型;
  • i32,有符號32位整型;
  • i64,有符號64位整型;
  • double,64位浮點數;
  • string,字符串;
  • binary,字節數組;

注意:thrift不支持無符號整型。

 

二、容器

  • map<t1,t2>,字典;
  • list<t1>,列表;
  • set<t1>,集合;

注意:容器中的元素類型能夠是除了service 之外的任何合法thrift類型(包括結構體和異常)。

 

三、結構體 struct

Thrift結構體在概念上同C語言結構體類型—-一種將相關屬性彙集(封裝)在一塊兒的方式;

在面嚮對象語言中,thrift結構體被轉換成類。

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

結構體中,每一個字段包含一個整數ID,數據類型、字段名,和一個可選的默認值。

字段還能夠聲明爲"optional",當該字段沒有設置的時候,不會被序列化輸出;

規範的struct定義中的每一個域均會使用required或者optional關鍵字進行標識。若是required標識的域沒有賦值,thrift將給予提示。若是optional標識的域沒有賦值,該域將不會被序列化傳輸。若是某個optional標識域有缺省值而用戶沒有從新賦值,則該域的值一直爲缺省值。

 

四、異常 exception

異常在語法和功能上相似於結構體,只不過異常使用關鍵字exception而不是struct關鍵字聲明。但它在語義上不一樣於結構體,當定義一個RPC服務時,開發者可能須要聲明一個遠程方法拋出一個異常。

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

 

五、服務 service 

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

Thrift編譯器會根據選擇的目標語言爲server產生服務接口代碼,爲client產生樁代碼。

複製代碼
//「Twitter」與「{」之間須要有空格!!!
service Twitter {
 
// 方法定義方式相似於C語言中的方式,它有一個返回值,一系列參數和可選的異常
 
// 列表. 注意,參數列表和異常列表定義方式與結構體中域定義方式一致.
 
void ping(),                       // 函數定義可使用逗號或者分號標識結束
 
bool postTweet(1:Tweet tweet);    // 參數能夠是基本類型或者結構體,參數是隻讀的(const),不能夠做爲返回值!!!
 
TweetSearchResult searchTweets(1:string query); // 返回值能夠是基本類型或者結構體
 
// 」oneway」標識符表示client發出請求後沒必要等待回覆(非阻塞)直接進行下面的操做,
 
// 」oneway」方法的返回值必須是void
 
oneway void zip()               // 返回值能夠是void
 
}
複製代碼

service中的函數,其參數列表的定義方式與struct徹底同樣;

service支持繼承,一個service可以使用extends關鍵字繼承另外一個service,struct不支持繼承;

 

六、枚舉類型 enum

複製代碼
enum TweetType {
 
TWEET,         // 編譯器默認從1開始賦值
RETWEET = 2,  // 能夠賦予某個常量某個整數
DM = 0xa,     //容許常量是十六進制整數
REPLY         // 末尾沒有逗號
}        

struct Tweet { 1: required i32 userId; 2: required string userName; 3: required string text; 4: optional Location loc; 5: optional TweetType tweetType = TweetType.TWEET // 給常量賦缺省值時,使用常量的全稱 16: optional string language = "english" }
複製代碼

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

 

七、常量 const

Thrift容許用戶定義常量,複雜的類型和結構體可以使用JSON形式表示。

const i32 INT_CONST = 1234;    // 分號是可選的
const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}

 

PS:跟C語言相似,Thrift也支持typedef語句,例如:

typedef i32 MyInteger 

 

命名空間

Thrift中的命名空間同C++中的namespace相似,它們均提供了一種組織(隔離)代碼的方式。由於每種語言均有本身的命名空間定義方式(如python中有module),thrift容許開發者針對特定語言定義namespace:

namespace cpp com.example.project 
namespace java com.example.project 

 


 

產生代碼

下面介紹Thrift產生各類目標語言代碼的方式,

 

Thrift的網絡棧以下所示:

 

Transport層提供了一個簡單的網絡讀寫抽象層。這使得thrift底層的transport從系統其它部分(如:序列化/反序列化)解耦。

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

複製代碼
open
close
read
write
listen
accept
flush
複製代碼

 

Protocol抽象層定義了一種將內存中數據結構映射成可傳輸格式的機制。換句話說,Protocol定義了datatype怎樣使用底層的Transport對本身進行編解碼。所以,Protocol的實現要給出編碼機制並負責對數據進行序列化。

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封裝了從輸入數據流中讀數據和向數據數據流中寫數據的操做。讀寫數據流用Protocol對象表示。Processor的結構體很是簡單:

interface TProcessor {
 
bool process(TProtocol in, TProtocol out) throws TException
 
}

與服務相關的processor實現由編譯器產生。Processor主要工做流程以下:從鏈接中讀取數據(使用輸入protocol),將處理受權給handler(由用戶實現),最後將結果寫到鏈接上(使用輸出protocol)。

 

Server將以上全部特性集成在一塊兒:
(1) 建立一個transport對象
(2) 爲transport對象建立輸入輸出protocol
(3) 基於輸入輸出protocol建立processor
(4) 等待鏈接請求並將之交給processor處理

 

 

 

 原文連接:https://www.cnblogs.com/chenny7/p/4224720.html

 

 

 

參考文檔:

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/

thrift 的原理和使用

 

Thrift 架構 

Thrift是一個跨語言的服務部署框架,最初由Facebook於2007年開發,2008年進入Apache開源項目。Thrift經過IDL(Interface Definition Language,接口定義語言)來定義RPC(Remote Procedure Call,遠程過程調用)的接口和數據類型,而後經過thrift編譯器生成不一樣語言的代碼(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),並由生成的代碼負責RPC協議層和傳輸層的實現。

PS:CentOS下的Thrift的安裝流程能夠參考這裏

 

Thrift架構

 

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

  • TBinaryProtocol:二進制格式;
  • TCompactProtocol:壓縮格式;
  • TJSONProtocol:JSON格式;
  • TSimpleJSONProtocol:提供JSON只寫協議, 生成的文件很容易經過腳本語言解析;
  • TDebugProtocol:使用易懂的可讀的文本格式,以便於debug

TTransport(傳輸層),定義數據傳輸方式,能夠爲TCP/IP傳輸,內存共享或者文件共享等)被用做運行時庫。

  • TSocket:阻塞式socker;
  • TFramedTransport:以frame爲單位進行傳輸,非阻塞式服務中使用;
  • TFileTransport:以文件形式進行傳輸;
  • TMemoryTransport:將內存用於I/O,java實現時內部實際使用了簡單的ByteArrayOutputStream;
  • TZlibTransport:使用zlib進行壓縮, 與其餘傳輸方式聯合使用,當前無java實現;

 

Thrift支持的服務模型

  • TSimpleServer:簡單的單線程服務模型,經常使用於測試;
  • TThreadPoolServer:多線程服務模型,使用標準的阻塞式IO;
  • TNonblockingServer:多線程服務模型,使用非阻塞式IO(需使用TFramedTransport數據傳輸方式);

 

Thrift其實是實現了C/S模式,經過代碼生成工具將thrift文生成服務器端和客戶端代碼(能夠爲不一樣語言),從而實現服務端和客戶端跨語言的支持。用戶在Thirft文件中聲明本身的服務,這些服務通過編譯後會生成相應語言的代碼文件,而後客戶端調用服務,服務器端提服務即可以了。

 

通常將服務放到一個.thrift文件中,服務的編寫語法與C語言語法基本一致,在.thrift文件中有主要有如下幾個內容:變量聲明(variable)、數據聲明(struct)和服務接口聲明(service, 能夠繼承其餘接口)。

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

複製代碼
// 包含頭文件
include 「shared.thrift」        

// 指定目標語言
namespace cpp tutorial            

// 定義變量
const i32 INT32CONSTANT = 9853        

// 定義結構體
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

// 定義服務
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 --gen cpp tutorial.thrift   #結果代碼存放在gen-cpp目錄下

 

若是是要生成java代碼:

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

 

client端和sever端代碼要調用編譯.thrift生成的中間文件。
下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代碼

 

 

 

在client端,用戶自定義CalculatorClient類型的對象(用戶在.thrift文件中聲明的服務名稱是Calculator, 則生成的中間代碼中的主類爲CalculatorClient), 該對象中封裝了各類服務,能夠直接調用(如client.ping()), 而後thrift會經過封裝的rpc調用server端同名的函數。
在server端,須要實如今.thrift文件中聲明的服務中的全部功能,以便處理client發過來的請求。

 


 

Thrift語法

Thrift文件支持shell命令,所以thrift是可執行的。

Thrfit支持shell註釋風格(#),也支持C/C++語言中單行(//)或者多行(/* */)註釋風格

 

數據類型

一、基本類型

  • bool,布爾型,1個字節;
  • byte,有符號單字節;
  • i16,有符號16位整型;
  • i32,有符號32位整型;
  • i64,有符號64位整型;
  • double,64位浮點數;
  • string,字符串;
  • binary,字節數組;

注意:thrift不支持無符號整型。

 

二、容器

  • map<t1,t2>,字典;
  • list<t1>,列表;
  • set<t1>,集合;

注意:容器中的元素類型能夠是除了service 之外的任何合法thrift類型(包括結構體和異常)。

 

三、結構體 struct

Thrift結構體在概念上同C語言結構體類型—-一種將相關屬性彙集(封裝)在一塊兒的方式;

在面嚮對象語言中,thrift結構體被轉換成類。

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

結構體中,每一個字段包含一個整數ID,數據類型、字段名,和一個可選的默認值。

字段還能夠聲明爲"optional",當該字段沒有設置的時候,不會被序列化輸出;

規範的struct定義中的每一個域均會使用required或者optional關鍵字進行標識。若是required標識的域沒有賦值,thrift將給予提示。若是optional標識的域沒有賦值,該域將不會被序列化傳輸。若是某個optional標識域有缺省值而用戶沒有從新賦值,則該域的值一直爲缺省值。

 

四、異常 exception

異常在語法和功能上相似於結構體,只不過異常使用關鍵字exception而不是struct關鍵字聲明。但它在語義上不一樣於結構體,當定義一個RPC服務時,開發者可能須要聲明一個遠程方法拋出一個異常。

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

 

五、服務 service 

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

Thrift編譯器會根據選擇的目標語言爲server產生服務接口代碼,爲client產生樁代碼。

複製代碼
//「Twitter」與「{」之間須要有空格!!!
service Twitter {
 
// 方法定義方式相似於C語言中的方式,它有一個返回值,一系列參數和可選的異常
 
// 列表. 注意,參數列表和異常列表定義方式與結構體中域定義方式一致.
 
void ping(),                       // 函數定義可使用逗號或者分號標識結束
 
bool postTweet(1:Tweet tweet);    // 參數能夠是基本類型或者結構體,參數是隻讀的(const),不能夠做爲返回值!!!
 
TweetSearchResult searchTweets(1:string query); // 返回值能夠是基本類型或者結構體
 
// 」oneway」標識符表示client發出請求後沒必要等待回覆(非阻塞)直接進行下面的操做,
 
// 」oneway」方法的返回值必須是void
 
oneway void zip()               // 返回值能夠是void
 
}
複製代碼

service中的函數,其參數列表的定義方式與struct徹底同樣;

service支持繼承,一個service可以使用extends關鍵字繼承另外一個service,struct不支持繼承;

 

六、枚舉類型 enum

複製代碼
enum TweetType {
 
TWEET,         // 編譯器默認從1開始賦值
RETWEET = 2,  // 能夠賦予某個常量某個整數
DM = 0xa,     //容許常量是十六進制整數
REPLY         // 末尾沒有逗號
}        

struct Tweet { 1: required i32 userId; 2: required string userName; 3: required string text; 4: optional Location loc; 5: optional TweetType tweetType = TweetType.TWEET // 給常量賦缺省值時,使用常量的全稱 16: optional string language = "english" }
複製代碼

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

 

七、常量 const

Thrift容許用戶定義常量,複雜的類型和結構體可以使用JSON形式表示。

const i32 INT_CONST = 1234;    // 分號是可選的
const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}

 

PS:跟C語言相似,Thrift也支持typedef語句,例如:

typedef i32 MyInteger 

 

命名空間

Thrift中的命名空間同C++中的namespace相似,它們均提供了一種組織(隔離)代碼的方式。由於每種語言均有本身的命名空間定義方式(如python中有module),thrift容許開發者針對特定語言定義namespace:

namespace cpp com.example.project 
namespace java com.example.project 

 


 

產生代碼

下面介紹Thrift產生各類目標語言代碼的方式,

 

Thrift的網絡棧以下所示:

 

Transport層提供了一個簡單的網絡讀寫抽象層。這使得thrift底層的transport從系統其它部分(如:序列化/反序列化)解耦。

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

複製代碼
open
close
read
write
listen
accept
flush
複製代碼

 

Protocol抽象層定義了一種將內存中數據結構映射成可傳輸格式的機制。換句話說,Protocol定義了datatype怎樣使用底層的Transport對本身進行編解碼。所以,Protocol的實現要給出編碼機制並負責對數據進行序列化。

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封裝了從輸入數據流中讀數據和向數據數據流中寫數據的操做。讀寫數據流用Protocol對象表示。Processor的結構體很是簡單:

interface TProcessor {
 
bool process(TProtocol in, TProtocol out) throws TException
 
}

與服務相關的processor實現由編譯器產生。Processor主要工做流程以下:從鏈接中讀取數據(使用輸入protocol),將處理受權給handler(由用戶實現),最後將結果寫到鏈接上(使用輸出protocol)。

 

Server將以上全部特性集成在一塊兒:
(1) 建立一個transport對象
(2) 爲transport對象建立輸入輸出protocol
(3) 基於輸入輸出protocol建立processor
(4) 等待鏈接請求並將之交給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/

相關文章
相關標籤/搜索