閱讀1001 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Apache Thrift使用總結

使用感受

之前對Thrift的理解有點不準確,使用之後發現Thrift比想象中的要簡單得多。

Thrift做的事情就是跨語言的分布式RPC,通過編寫.thrift文件聲明接口類和方法,客戶端調用定義的方法,Server端實現定義的接口。雖然的確RPC是需要網絡請求,但不像Netty這種NIO網絡編程庫(還要關注很多數據傳輸中的細節,比如數據如何序列化、如何在字節數組裏建立結構、如何在兩端解析字節數組、如何處理Handler裏的事件狀態、如何把多個Handler按順序串起來),Thrift掩蓋了數據傳輸這件事情,開發者使用的時候就是純純的RPC的使用感受。


基本使用

Thrift使用起來幾乎沒有任何門檻,可以看這篇HelloWorld的文章,雖然有點老,但是看完之後基本使用起來沒有任何障礙了。

官方給出的這個例子更加全麵些,全麵在.thrift文件裏可以聲明的東西列的更全些。

下麵看看兩個.thrift的定義:

shared.thrift

/**
 * This Thrift file can be included by other Thrift files that want to share
 * these definitions.
 */

namespace java com.baidu.mordor.sink.service

struct SharedStruct {
  1: i32 key
  2: string value
}

service SharedService {
  SharedStruct getStruct(1: i32 key)
}

tutorial.thrift

/**
 * The first thing to know about are types. The available types in Thrift are:
 *
 *  bool        Boolean, one byte
 *  byte        Signed byte
 *  i16         Signed 16-bit integer
 *  i32         Signed 32-bit integer
 *  i64         Signed 64-bit integer
 *  double      64-bit floating point value
 *  string      String
 *  binary      Blob (byte array)
 *  map<t1,t2>  Map from one type to another
 *  list<t1>    Ordered list of one type
 *  set<t1>     Set of unique elements of one type
 *
 * Did you also notice that Thrift supports C style comments?
 */

// Just in case you were wondering... yes. We support simple C comments too.

/**
 * Thrift files can reference other Thrift files to include common struct
 * and service definitions. These are found using the current path, or by
 * searching relative to any paths specified with the -I compiler flag.
 *
 * Included objects are accessed using the name of the .thrift file as a
 * prefix. i.e. shared.SharedObject
 */
include "shared.thrift"

/**
 * Thrift files can namespace, package, or prefix their output in various
 * target languages.
 */
namespace java com.baidu.mordor.sink.service


/**
 * Thrift lets you do typedefs to get pretty names for your types. Standard
 * C style here.
 */
typedef i32 MyInteger

/**
 * Thrift also lets you define constants for use across languages. Complex
 * types and structs are specified using JSON notation.
 */
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}

/**
 * You can define enums, which are just 32 bit integers. Values are optional
 * and start at 1 if not supplied, C style again.
 */
enum Operation {
  ADD = 1,
  SUBTRACT = 2,
  MULTIPLY = 3,
  DIVIDE = 4
}

/**
 * Structs are the basic complex data structures. They are comprised of fields
 * which each have an integer identifier, a type, a symbolic name, and an
 * optional default value.
 *
 * Fields can be declared "optional", which ensures they will not be included
 * in the serialized output if they aren't set.  Note that this requires some
 * manual management in some languages.
 */
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

/**
 * Structs can also be exceptions, if they are nasty.
 */
exception InvalidOperation {
  1: i32 what,
  2: string why
}

/**
 * Ahh, now onto the cool part, defining a service. Services just need a name
 * and can optionally inherit from another service using the extends keyword.
 */
service Calculator extends shared.SharedService {

   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通過IDL(接口定義語言),在.thrift文件裏聲明接口類和方法,聲明struct結構、const、Exception等,還可以include別的.thrift文件,這套語法與C非常相似。通過編寫IDL和generate代碼,做到了不同語言之間的RPC,客戶端實現接口類和使用結構類的時候非常簡單好用。
上麵例子的代碼可以從官方下載到,可以放到本地看一下他的使用,非常簡單。


Thrift重要組件

Thrift API裏三個重要組成部分:Protocal,Transport,Server。


Protocal定義了消息如何序列化。常見的是TBinaryProtocol,TJSONProtocol,TCompactProtocol。


Transport定義了消息在客戶端和服務端如何通信。常見的是TSocket,TFramedTransport,TNonblockingTransport等。


Server從transport端接收序列化後的消息,根據protocal反序列化回來,然後調用用戶實現的消息handler(接口實現類),最後把返回的數據序列化後再傳回給客戶端。常見的TServer為TSimpleServer,THsHaServer,TThreadPoolServer,TNonBlockingServer,TThreadedSelectorServer。下麵會具體介紹各個Server的特點,開發者需要選擇適合自己場景的一套 Server+對應的Transport+對應的Protocol。


TServer說明

Thrift實現的幾種不同的TServer。對於Java而言,版本按0.9.0為準:

TSimpleServer在sever端隻有一個I/O阻塞的單線程,每次隻接受並服務一個客戶端,適合測試使用,不能用於線上服務。


TNonblockingServer修改了TSimpleServer裏阻塞的缺點,借助NIO裏的Selector實現非阻塞I/O,允許多個客戶端連接並且客戶端可以使用select()選擇。但是處理消息和select()的是同一個線程,當有大量客戶端連接的時候,性能是不理想的。


 THsHaServer(半同步半異步server)在以上基礎上,使用一個單獨線程來處理網絡I/O,一個worker線程池來處理消息。好處是隻要有空閑worker線程,消息可以被及時、並行處理,吞吐量會大一些。


TThreadedSelectorServer,與THsHaServer的區別是處理網絡I/O也是多線程了,它維護兩個線程池,一個負責網絡I/O,一個負責數據處理。優點是當網絡I/O是瓶頸的情況下,性能比THsHaServer更好。


TThreadPoolServer有一個專用的線程來接收connections,連接被建立後,會從ThreadPoolExecutor裏取一個工作線程來負責這次連接,直到連接斷開後線程回到線程池裏,且線程池大小可配。也就是說,並發性的大小可根據服務器設定,如果不介意開很多線程的話,TThreadPoolServer是個還不錯的選擇。


全文完 :)


最後更新:2017-04-03 07:57:02

  上一篇:go mina的編碼和解碼以及斷包的處理,發送自定義協議,仿qq聊天,發送xml或json
  下一篇:go the solution about &quot;messy code&quot; in elicpse