Thrift是apache的一個開源框架,此框架抽取出C/S的必要元素,將基本的網絡通信進行封裝,降低了網絡開發的難度。在thrift文件的基礎上,使用Thrift提供的工具(thrift等)可以自動生成程序的框架代碼,使程序員將注意力只集中在通信接口以及C/S各自的邏輯上。
thrift文件
thrift文件是定義C/S通信的接口文件,在此文件中定義了server提供哪些遠程訪問接口(service),以及需要操作哪些類型的數據(struct),以及在操作過程中出現的異常(exception)。
1. service
thrift會在server端的代碼中自動生成service的接口(未實現),程序員在其中實現需要提供的服務邏輯即可,同時client端代碼中也會實現與service同名的接口(已實現),用於給串行化需要給server端傳遞的數據。例如,在thrift中定義瞭如下service:
service HeartBeatProtocal { HeartBeatResponse heartbeat(1: HeartBeatMessage msg) }
通過thrift自動生成的代碼中,service端就會有如下的接口生成:
void heartbeat(HeartBeatResponse& _return, const HeartBeatMessage& msg) { // Your implementation goes here }
client端則會有如下同名接口的實現:
void HeartBeatProtocalClient::heartbeat(HeartBeatResponse& return, const HeartBeatMessage& msg) { sendheartbeat(msg); recvheartbeat(return); }
因此程序員需要完成的工作僅有兩點:
實現server端的service接口;
在client程序中調用與service接口同名的函數來完成通信
2. struct
C/S程序必然是為了完成信息的交互,無論是server給client提供請求的數據,還是client向server傳送需要存儲的數據,其中數據是核心。在thrift數據的格式就是由一些內置的基礎數據類型,以及struct組成複合數據類型來組成。
thrift內置了大多數用到的數據類型,具體如下:
bool bool型變量
byte 有符號字符型變量
i16 有符號16位整形變量
i32 有符號32位整形變量
i64 有符號64位整形變量
double 64位寬的浮點型變量
string C++中的string類變量
binary 二進制數據,一系列byte型變量的集合
map
key-value型變量
list
有序列表
set
集合,集合中的每個元素唯一
通過struct將上述基礎數據類型組合起來就形成了程序中需要傳輸的數據類型。struct格式如下:
struct HostInfo { 1: string hostname, 2: string ipaddr }
thrift會為自定義的struct生成對應的class,以及相應的接口函數。
thrift工具
完成了thrift文件的編寫後,需要藉助thrift來自動生成相應的代碼。命令具體格式如下:
thrift --gen program_language thrift_file
示例
為了試用thrift,下面實現一個簡單的master-agent模式的程序。agent負責在服務器上搜集配置信息,例如主機名、CPU信息、內存信息等,master負責在遠程服務器上彙總各agent的信息,並存儲起來。
1. 編寫thrift文件
在這個例子中,master僅需要提供一個服務,就是接受agent傳送過來的數據並存儲起來。所以,需要在thrift文件中定義一個service,它需要接受agent傳送過來的信息,並給agent返回一個時間戳表示數據已成功接收到並存儲起來:
service HeartBeatProtocal { HeartBeatResponse heartbeat(1: HeartBeatMessage msg) }
如上所述,其接口需要兩個數據類型,一個是agent傳送給master的基礎信息數據HeartBeatMessage,另一個是master返回給agent的相關信息HeartBeatResponse,因此在thrift中定義相關的struct:
struct HostInfo { 1: string hostname, 2: string ipaddr }
struct CpuInfo { 1: i32 cpuNum, 2: string cpuModel }
struct DimmInfo { 1: i32 dimmCap, 2: string dimmModel }
struct MemInfo { 1: i32 memNum, 2: map dimmInfo }
struct HeartBeatMessage { 1: i32 timestamp, 2: HostInfo hostInfo, 3: CpuInfo cpuInfo, 4: MemInfo memInfo, 5: bool firstHeartBeat }
struct HeartBeatResponse { 1: i32 timestamp }
2. 自動生成代碼
運行thrift --gen cpp HeartBeat.thrift,在gen-cpp目錄下即生成了相關的代碼。
3. 實現服務端邏輯
在本實例中,server僅提供一個service heartbeat,因此在HeartBeatProtocalServer.cpp文件中(thrift生成的文件原名為HeartBeatProtocal_server.skeleton.cpp)的HeartBeatProtocalHandler類中實現heartbeat接口:
void heartbeat(HeartBeatResponse& _return, const HeartBeatMessage& msg) {
// Your implementation goes here
}
4. 實現客戶端程序
thrift是不會自動生成客戶端程序的,為了實現客戶端程序,需要對thrift中通信的類和接口有個初步的瞭解,相關知識將在另一片博客中介紹,這裡僅提供相關代碼:
int main(int argc, char *argv[]) { boost::sharedptr socket(new TSocket("10.32.64.221", 9090)); boost::sharedptr transport(new TBufferedTransport(socket)); boost::shared_ptr protocol(new TBinaryProtocol(transport));
HeartBeatProtocalClient client(protocol);
}
5. 實現Makefile文件
GENSRC := HeartBeatconstants.cpp HeartBeatProtocal.cpp HeartBeattypes.cpp getinfo.cpp GENOBJ := $(patsubst %.cpp, %.o, $(GENSRC))
INCLUDEDIR := /home/lsy/third-64/thrift/include/ LIBDIR := /home/lsy/third-64/thrift/lib/
.PHONY: all clean
all: HeartBeatProtocalServer HeartBeatProtocalClient
%.o: %.cpp $(CXX) -Wall -DHAVEINTTYPESH -DHAVENETINETINH -I$(INCLUDEDIR) -c $< -o $@
HeartBeatProtocalServer: HeartBeatProtocalServer.o $(GENOBJ) $(CXX) $^ -o $@ -L$(LIBDIR) -lthrift
HeartBeatProtocalClient: HeartBeatProtocalClient.o $(GENOBJ) $(CXX) $^ -o $@ -L$(LIBDIR) -lthrift
clean: $(RM) *.o HeartBeatProtocalServer
總結
thrift的確簡化了網絡通信的大量操作,使程序員能集中精力在具體的邏輯,以及數據交互格式上,另外由於使用經過認證的框架,也大大提高了程序的可靠性。