thrift總結(python)

總結

1.首先要編寫一個*.thrift文件,這個文件裏面定義了結構體和方法

舉例:

類似於java中的屬性體和接口

2.定義完這個文件之後,確定你要做哪一端,在客戶端或者服務器端都要得到此文件,然後運行sudo thrift --gen py *.thrift(有時候文件夾生成不成功,有時候成功,很奇怪還在找原因)

3.此時在當前目錄會生成gen-py文件

4.此時不用管這個文件夾,就可以在編寫自己的客戶端或者服務器端的py文件了,新建一個py文件,見下面例子:

注:此處引用別人的,客戶端:

#!/usr/bin/env python
 
import sys
sys.path.append('./gen-py')
 
from helloworld import HelloWorld
 
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
 
try:
  transport = TSocket.TSocket('localhost', 9090)
  transport = TTransport.TBufferedTransport(transport)
  protocol = TBinaryProtocol.TBinaryProtocol(transport)
  client = HelloWorld.Client(protocol)
  transport.open()
 
  print "client - ping"
  print "server - " + client.ping()
 
  print "client - say"
  msg = client.say("Hello!")
  print "server - " + msg
 
  transport.close()
 
except Thrift.TException, ex:
  print "%s" % (ex.message)
服務器端:

#!/usr/bin/env python
 
import socket
import sys
sys.path.append('./gen-py')
 
from helloworld import HelloWorld
from helloworld.ttypes import *
 
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
 
class HelloWorldHandler:
  def ping(self):
    return "pong"
 
  def say(self, msg):
    ret = "Received: " + msg
    print ret
    return ret
 
handler = HelloWorldHandler()
processor = HelloWorld.Processor(handler)
transport = TSocket.TServerSocket("localhost", 9090)
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
 
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
 
print "Starting thrift server in python..."
server.serve()
print "done!"

1.Thrift有以下幾個概念:

類型系統(typesystem)

Thrift定義了一套數據傳輸描述語言(有點類似IDL),它是“語言中性”的,這個就是它的類型系統。它分爲五種類型(數據類型表達3種,預定義類/結構1種,接口表達1種):

基本類型(basictype),也就是bool、byte、i16、i32、i64、double、string,任何語言都有這些基本類型,比較有意思的是string,它即表達text,也表達binary bytes。另一個特點是整型沒有unsigned,原因比較簡單,因爲有些語言不支持。

  • 結構類型(struct):就是C語言中的struct,將基本類型組合起來。
  • 容器類型(container):就是集合類型(list/set/map),其中的元素是任何Thrift可識別的基本、結構、容器類型。【不知道是否有不支持list/set/map的語言,那麼Thrift如何處理呢?】
  • 異常類型(exception):從數據結構講就是結構類型,可以認爲是便於異常的處理而單獨拿出來的、預定義的、有特殊意義的結構類型。
  • 服務定義類型(service):這個類型實際是用來定義接口的,Thrift代碼生成器會根據這個定義,生成代碼框架。

傳輸(transport)

也就是信息的傳輸渠道以及讀寫方式,例如,介質可以是socket、shared memory或file,Thrift規定了一些基本的操作(open/close/isOpen/read/write/flush,對server,再加上listen/accept)。特別的,針對Socket方式,有TScoket類,對file方式,有TFileTransport類,上面類比較底層,還有幾個實用的類:TBufferedTransport,TFramedTransport,TMemoryBuffer等。

 

協議(protocol)

是對傳輸協議的封裝,也就是傳輸採用二進制、XML或者text來表示信息,它的功能有兩個:1.雙向的消息隊列;2.信息的編碼和解碼(也就是對上面類型的讀/寫)。關於流式格式,thrift數據類型是自我分割的,意思是說,thrift會自己在數據域的分割處插入標誌,在解碼的時候,即使沒有數據域定義,thrift也能成功分割出各數據域。在若干篇文章中,都提到thrift的二進制流式編碼有相當的效率(可以配合壓縮),因此首選的協議應該是binary協議。

 

版本(versioning)

如果一個程序分開來開發,那版本問題就是繞不過去的問題。Thrift的版本是通過“field identifiers”來實現的,每個結構由其標識,結構中的每個域有其標識,這兩個標識唯一決定了一個數據域。在解碼的時候,數據域的標識被檢查,如果不能識別,則該數據域被拋棄。Thrift也可以通過”Isset”機制來明確某些域的設置與否(發送端用來指明是否設置,接收端用來檢測是否設置)。

四種情況:

  1. 添加了數據域, 舊客戶端,新服務器端:客戶端發送的數據中沒有該域,服務器端能檢測出來,可按缺省值處理。
  2. 刪除了數據域, 舊客戶端,新服務器端:客戶端發送的數據中有該域,服務器端忽略該域。
  3. 添加了數據域, 新客戶端,舊服務器端:客戶端發送的數據中有該域,服務器端忽略該域。
  4. 刪除了數據域, 新客戶端,舊服務器端:客戶端發送的數據中沒有該域,服務器端可能不知道如何處理這種情況。

 處理器(processor)

就是如何將各部分協調起來,形成代碼(或用戶代碼的框架)。它有兩個重要的類:TProcessor和TServer。TProcessor用來實現RPC調用,TServer是所有Server類的基類,TServer類主要處理連接和線程,而不管諸如傳輸、編碼等。用戶代碼主要關注的一是.thrift文件,二就是這個接口。Thrift爲此實現了TSimpleServer(單線程), TThreadedServer(每連接一個線程)和 TThreadPoolServer(線程池)等類。

下圖是thrift生成代碼的基本結構(C++)。


圖中,ServiceIf是根據接口文件(.thrift)生成的虛接口類,用戶的具體實現在ServiceHandler中。各種調用方式在TServer中實現。【詳細的描述見實例】

 

2.Thrift實現上的幾個考慮

目標語言

雖然有多種選擇,但最常用的(可能也是支持最好的)是C++, Java, and Python。


生成的結構體

數據域成員都是公有的,沒有set,get之類的東西,雖然建議採用isset,但也可以不用,系統足夠強健來處理類似“FieldNotSetException”之類的問題,因而也沒有涉及該異常。Read和write方面也是公有的,這樣用戶可以在固有的RPC之外來使用它們。

RPC方法標識:實現RPC時,建立函數名與函數指針之間的映射,大致如下(不同的語言表達方式不同,C++,map):

std::map<std::string,函數指針> processMap_;

這樣加快函數調用。


多線程

對C++實現,在開發過程中,thrift開發人員研究過boost,ACE中與thread,timer相關的東西,開發人員不想引入過多的第三方依賴,因此thrift中只有對boost::shared_ptr的引用是必須的,但爲了跨平臺或獲得更多的功能,一般情況下,boost中thread,timer及其依賴庫也是需要的。


ThreadManager和TimerManager

線程管理類用來管理線程池,定時器管理類可以定時觸發Runnable的對象,開啓一件事情(可以放到或不放到一個單獨線程)。


NonblockingOperation

這個東西需要libevent的支持。


Compiler(代碼生成器)

這個東西是用C++寫的,依賴於lex/yacc。代碼生成分兩步:第一,檢查包含的文件和類型定義文件,生成“解析樹”(the parse tree);第二,將各類型放到解析樹中,根據解析樹生成代碼。


TFileTransport

這個類(及其繼承類)可以將request消息記入文件,爲提高性能,它先緩存記錄,並存入磁盤。記錄文件是分塊的(文件固定大小),採用padding,記錄不能跨塊。





發佈了25 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章