Thrift

Thrift 的總體架構html

Thrift 包含一個完整的堆棧結構用於構建客戶端和服務器端。其中代碼框架層是根據 Thrift 定義的服務接口描述文件生成的客戶端和服務器端代碼框架,數據讀寫操做層是根據 Thrift 文件生成代碼實現數據的讀寫操做java

clipboard.png

Thrift 包含三個主要的組件:protocol,transport 和 server。緩存

  • protocol 定義了消息是怎樣序列化的;安全

  • transport 定義了消息是怎樣在客戶端和服務器端之間通訊的;服務器

  • server 用於從 transport 接收序列化的消息,根據 protocol
    反序列化之,調用用戶定義的消息處理器,並序列化消息處理器的響應,而後再將它們寫回 transport。網絡

TProtocol(協議層) 定義數據傳輸格式架構

  • TBinaryProtocol:二進制編碼格式進行數據傳輸;併發

  • TCompactProtocol:壓縮的、密集的數據傳輸協議,基於Variable-length quantity的zigzag 編碼格式;框架

  • TJSONProtocol:以JSON數據編碼協議進行數據傳輸;異步

  • TSimpleJSONProtocol:提供JSON只寫協議, 生成的文件很容易經過腳本語言解析;

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

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

  • TSocket:阻塞型 socket,用於客戶端,採用系統函數 read 和 write 進行讀寫數據。

  • TServerSocket:非阻塞型 socket,用於服務器端,accecpt 到的 socket 類型都是 TSocket(即阻塞型 socket)。

  • TBufferedTransport 和 TFramedTransport 都是有緩存的,均繼承TBufferBase,調用下一層 TTransport 類進行讀寫操做嗎,結構極爲類似。其中 TFramedTransport 以幀爲傳輸單位,幀結構爲:4個字節(int32_t)+傳輸字節串,頭4個字節是存儲後面字節串的長度,該字節串纔是正確須要傳輸的數據,所以 TFramedTransport 每傳一幀要比 TBufferedTransport 和 TSocket 多傳4個字節。

  • TMemoryBuffer 繼承 TBufferBase,用於程序內部通訊用,不涉及任何網絡I/O,可用於三種模式:
    (1)OBSERVE模式,不可寫數據到緩存;(2)TAKE_OWNERSHIP模式,需負責釋放緩存;(3)COPY模式,拷貝外面的內存塊到TMemoryBuffer;

  • TFileTransport 直接繼承 TTransport,用於寫數據到文件。對事件的形式寫數據,主線程負責將事件入列,寫線程將事件入列,並將事件裏的數據寫入磁盤。這裏面用到了兩個隊列,類型爲 TFileTransportBuffer,一個用於主線程寫事件,另外一個用於寫線程讀事件,這就避免了線程競爭。在讀完隊列事件後,就會進行隊列交換,因爲由兩個指針指向這兩個隊列,交換隻要交換指針便可。它還支持以 chunk(塊)的形式寫數據到文件。

  • TFDTransport 是很是簡單地寫數據到文件和從文件讀數據,它的 write 和 read 函數都是直接調用系統函數 write 和 read 進行寫和讀文件。

  • TSimpleFileTransport 直接繼承 TFDTransport,沒有添加任何成員函數和成員變量,不一樣的是構造函數的參數和在 TSimpleFileTransport 構造函數裏對父類進行了初始化(打開指定文件並將fd傳給父類和設置父類的close_policy爲CLOSE_ON_DESTROY)。

  • TZlibTransport 跟 TBufferedTransport 和 TFramedTransport同樣,調用下一層 TTransport 類進行讀寫操做。它採用<zlib.h>提供的 zlib 壓縮和解壓縮庫函數來進行壓解縮,寫時先壓縮再調用底層 TTransport 類發送數據,讀時先調用 TTransport 類接收數據再進行解壓,最後供上層處理。

  • TSSLSocket 繼承 TSocket,阻塞型 socket,用於客戶端。採用 openssl 的接口進行讀寫數據。checkHandshake()函數調用 SSL_set_fd 將 fd 和 ssl 綁定在一塊兒,以後就能夠經過 ssl 的 SSL_read和SSL_write 接口進行讀寫網絡數據。

  • TSSLServerSocket 繼承 TServerSocket,非阻塞型 socket, 用於服務器端。accecpt 到的 socket 類型都是 TSSLSocket 類型。

  • THttpClient 和 THttpServer 是基於 Http1.1 協議的繼承 Transport 類型,均繼承 THttpTransport,其中 THttpClient 用於客戶端,THttpServer 用於服務器端。二者都調用下一層 TTransport 類進行讀寫操做,均用到TMemoryBuffer 做爲讀寫緩存,只有調用 flush() 函數纔會將真正調用網絡 I/O 接口發送數據。

Thrift 的服務模型 Server

  • TSimpleServer

  • TNonblockingServer

  • THsHaServer

  • TThreadedSelectorServer

  • TThreadPoolServer

TSimpleServer
TSimplerServer 接受一個鏈接,處理鏈接請求,直到客戶端關閉了鏈接,它纔回去接受一個新的鏈接。正由於它只在一個單獨的線程中以阻塞 I/O 的方式完成這些工做,因此它只能服務一個客戶端鏈接,其餘全部客戶端在被服務器端接受以前都只能等待。
TSimpleServer 主要用於測試目的,不要在生產環境中使用它!

TNonblockingServer vs. THsHaServer
TNonblockingServer 使用非阻塞的 I/O 解決了 TSimpleServer 一個客戶端阻塞其餘全部客戶端的問題。它使用了 java.nio.channels.Selector,經過調用 select(),它使得你阻塞在多個鏈接上,而不是阻塞在單一的鏈接上。當一或多個鏈接準備好被接受/讀/寫時,select() 調用便會返回。TNonblockingServer 處理這些鏈接的時候,要麼接受它,要麼從它那讀數據,要麼把數據寫到它那裏,而後再次調用 select() 來等待下一個可用的鏈接。通用這種方式,server 可同時服務多個客戶端,而不會出現一個客戶端把其餘客戶端所有「餓死」的狀況。
然而,還有個棘手的問題:全部消息是被調用 select() 方法的同一個線程處理的。假設有10個客戶端,處理每條消息所需時間爲100毫秒,那麼,latency 和吞吐量分別是多少?當一條消息被處理的時候,其餘9個客戶端就等着被 select,因此客戶端須要等待1秒鐘才能從服務器端獲得迴應,吞吐量就是10個請求/秒。若是能夠同時處理多條消息的話,會很不錯吧?
所以,THsHaServer(半同步/半異步的 server)就應運而生了。它使用一個單獨的線程來處理網絡I/O,一個獨立的 worker 線程池來處理消息。這樣,只要有空閒的 worker 線程,消息就會被當即處理,所以多條消息能被並行處理。用上面的例子來講,如今的 latency 就是100毫秒,而吞吐量就是100個請求/秒。
爲了演示作了一個測試,有10客戶端和一個修改過的消息處理器——它的功能僅僅是在返回以前簡單地 sleep 100 毫秒。使用的是有10個 worker 線程的 THsHaServer。消息處理器的代碼看上去就像下面這樣:
public ResponseCode sleep() throws TException{

try {
   Thread.sleep(100);
} catch (Exception ex) {}
return ResponseCode.Success;

}
特別申明,本章節的測試結果摘自站外文章,詳情請看文末連接
clipboard.png
clipboard.png
結果正如咱們想像的那樣,THsHaServer 可以並行處理全部請求,而 TNonblockingServer 只能一次處理一個請求。

THsHaServer vs. TThreadedSelectorServer
Thrift 0.8 引入了另外一種 server 實現,即 TThreadedSelectorServer。它與 THsHaServer 的主要區別在於,TThreadedSelectorServer 容許你用多個線程來處理網絡 I/O。它維護了兩個線程池,一個用來處理網絡 I/O,另外一個用來進行請求的處理。當網絡 I/O 是瓶頸的時候,TThreadedSelectorServer 比 THsHaServer 的表現要好。爲了展示它們的區別進行一個測試,令其消息處理器在不作任何工做的狀況下當即返回,以衡量在不一樣客戶端數量的狀況下的平均 latency 和吞吐量。對 THsHaServer,使用32個 worker 線程;對 TThreadedSelectorServer,使用16個 worker 線程和16個 selector 線程。
clipboard.png
clipboard.png
結果顯示,TThreadedSelectorServer 比 THsHaServer 的吞吐量高得多,而且維持在一個更低的 latency 上。

TThreadedSelectorServer vs. TThreadPoolServer
最後,還剩下 TThreadPoolServer。TThreadPoolServer 與其餘三種 server 不一樣的是:
1.有一個專用的線程用來接受鏈接
2.一旦接受了一個鏈接,它就會被放入 ThreadPoolExecutor 中的一個 worker 線程裏處理。
3.worker 線程被綁定到特定的客戶端鏈接上,直到它關閉。一旦鏈接關閉,該 worker 線程就又回到了線程池中。
4.你能夠配置線程池的最小、最大線程數,默認值分別是5(最小)和 Integer.MAX_VALUE(最大)。
這意味着,若是有1萬個併發的客戶端鏈接,你就須要運行1萬個線程。因此它對系統資源的消耗不像其餘類型的 server 同樣那麼「友好」。此外,若是客戶端數量超過了線程池中的最大線程數,在有一個 worker 線程可用以前,請求將被一直阻塞在那裏。

咱們已經說過,TThreadPoolServer 的表現很是優異。在我正在使用的計算機上,它能夠支持1萬個併發鏈接而沒有任何問題。若是你提早知道了將要鏈接到你服務器上的客戶端數量,而且你不介意運行大量線程的話,TThreadPoolServer 對你多是個很好的選擇。

clipboard.png

clipboard.png

我想你能夠從上面的描述能夠幫你作出決定:哪種 Thrift server 適合你。

TThreadedSelectorServer 對大多數案例來講都是個安全之選。若是你的系統資源容許運行大量併發線程的話,建議你使用 TThreadPoolServer。

使用 Thrift 強大的代碼生成引擎來生成 java 代碼,並經過詳細的步驟實現 Thrift Server 和 Client 調用。
第一步:定義好 IDL 描述語言

clipboard.png

第二步:生成後的 文件

clipboard.png

第三步:pom.xml 中引入 Thrift 的依賴

clipboard.png

第四步:實現服務端Server

clipboard.png

第五步:實現異步客戶端Client

clipboard.png

clipboard.png
別忘了關閉,當初沒關閉,致使系統跑一會就掛了。

參考文章
http://www.cnblogs.com/cyfonl...

相關文章
相關標籤/搜索