和 Thrift 的一場美麗邂逅

?一. 與 Thrift 的初識

也許大多數人接觸 Thrift 是從序列化開始的。每次搜索 “java序列化” + “方式”、“對比” 或 “性能” 等關鍵字時,搜索引擎總是會返回一大堆有關各種序列化方式的使用方法或者性能對比的結果給你,而其中必定少不了 Thrift,并且其性能還不錯嘞,至少比那戰斗力只有1的渣渣 java 原生序列化要強很多(好吧原諒我的小情緒……)。

然而,我最初接觸 Thrift 卻是從公司的一個項目開始。

也就在去年的這個時候,我所在事業部發現幾個 UGC 社區的小廣告特別嚴重,Boss 要求所有社區必須接入公司的富媒體監控系統(負責公司所有業務的內容審核、處罰工作,以下簡稱監控系統),以實現?UGC 內容(包括文本、圖片、音視頻以及用戶頭像、昵稱等UserInfo)的準實時上報與垃圾信息的自動處理(如清理現場、賬號封禁等)。出于對業務服務的最小侵入、功能復用和流程統一等原則的考慮,抽象出介于業務系統和監控系統之間的接入系統,統一負責對數據的接收、上報、重推、搜索、結果查詢以及對監控系統處罰指令的轉發。該業務可簡單抽象成圖 1.1:

圖 1.1

由于監控系統使用 Thrift 提供服務,因此接入系統與監控系統之間的交互都使用 Thrift 協議。考慮到接入的便捷性,業務系統可以使用 Thrift 和 Http 兩種協議與接入系統交互。

當時是我一個人負責這個項目,由于對 Thrift 的認識還是0,且項目時間短,所以總體上項目是非常趕的,一開始以為自己難以在規定時間內完成,但想不到 Thrift 開發起來還真的是相當的便捷。系統按時上線了,至今也沒出什么幺蛾子。后來又通過學習進一步了解了 Thrift,深以為是個必須入手的技能。

好吧,至此算是和 Thrift 正式結緣了。

?

二. 所謂的 RPC

在了解 Thrift 之前,先來簡單科普一下什么是 RPC(遠程過程調用)。

先看下面這個栗子:

public void invoke(){String param1 = "my String 1";String param2 = "my String 2";String res = getStr(param1, param2);System.out.println("res=" + res)
}
private String getStr(String str1, String str2){return str1 + str2;
}

這是一個最簡單不過的本地函數調用代碼,調用方和被調用方都在一個程序內部,屬于進程內調用。

CPU 在執行調用時切換去執行被調用函數,執行完后再切換回來執行后續的代碼。對調用方而言,執行被調用函數時會阻塞(非異步情況下)直到調用函數執行完畢。過程如圖 2.1

圖 2.1

接下來看個 RPC 調用的栗子:

public void test(){TestQry.Client client = getClient("192.168.4.222", 7800, 5000);String param1 = "my String 1";String param2 = "my String 2";String res = client.getStr(param1, param2);System.out.println("res=" + res);
}
private TestQry.Client getClient(String ip, int port, int timeOut) throws Exception{TSocket tSocket = new TSocket();TTransport transport = new TFramedTransport(tSocket);tTransport.open();TProtocol protocol = new TBinaryProtocol(tTransport);return new TestQry.Client(protocol);
}

這是一個進程間調用,調用方和被調用方不在同一個進程(甚至不同的服務器或機房)。

進程間調用需要通過網絡來傳輸數據,調用方在執行 RPC 調用時會阻塞(非異步情況下)直到調用結果返回才繼續執行后續代碼。過程如圖 2.2

圖 2.2

一言以蔽之,RPC 是一種通過網絡從遠程計算機程序上請求服務的方式,它使得開發包括網絡分布式多程序在內的應用程序更加容易。

?

三. 不僅僅是個序列化工具

Thrift 最初是由 Facebook 開發用做系統內各語言之間的 RPC 通信的一個可擴展且跨語言的軟件框架,它結合了功能強大的軟件堆棧和代碼生成引擎,允許定義一個簡單的定義文件中的數據類型和服務接口,以作為輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務器通信的無縫跨編程語言。

Thrift 是 IDL 描述性語言的一個具體實現,適用于程序對程序靜態的數據交換,需要先確定好數據結構。

Thrift 是完全靜態化的,當數據結構發生變化時,必須重新編輯IDL文件、代碼生成再編譯載入的流程,跟其他IDL工具相比較可以視為是 Thrift 的弱項。Thrift 適用于搭建大型數據交換及存儲的通用工具,在大型系統中的內部數據傳輸上相對于 JSON 和 XML 無論在性能、傳輸大小上有明顯的優勢。

注意, Thrift 不僅僅是個高效的序列化工具,它是一個完整的 RPC 框架體系!

3.1 堆棧結構

如圖 3.1所示,Thrift 包含一個完整的堆棧結構用于構建客戶端和服務器端。

圖 3.1

其中代碼框架層是根據 Thrift 定義的服務接口描述文件生成的客戶端和服務器端代碼框架,數據讀寫操作層是根據 Thrift 文件生成代碼實現數據的讀寫操作。

3.2 client/server調用流程

首先來看下 Thrift 服務端是如何啟動并提供服務的,如下圖 3.2所示(點擊此處看大圖):

圖 3.2

上圖所示是 HelloServiceServer 啟動的過程,以及服務被客戶端調用時服務器的響應過程。我們可以看到,程序調用了 TThreadPoolServer 的 serve() 方法后,server 進入阻塞監聽狀態,其阻塞在 TServerSocket 的 accept()方法上。當接收到來自客戶端的消息后,服務器發起一個新線程處理這個消息請求,原線程再次進入阻塞狀態。在新線程中,服務器通過 TBinaryProtocol 協議讀取消息內容,調用 HelloServiceImpl 的 helloVoid() 方法,并將結果寫入 helloVoid_result 中傳回客戶端。
在服務啟動后,客戶端就開始調用其服務,如圖 3.3所示(點擊此處看大圖):

圖 3.3

上圖展示的是 HelloServiceClient 調用服務的過程,以及接收到服務器端的返回值后處理結果的過程。我們可以看到,程序調用了 Hello.Client 的 helloVoid() 方法,在 helloVoid() 方法中,通過 send_helloVoid() 方法發送對服務的調用請求,通過 recv_helloVoid() 方法接收服務處理請求后返回的結果。

3.3 數據類型

上一節我們已經大致了解了 Thrift 的 server 和 client 的工作流程,現在就來講講 Thrift 可定義的數據類型。Thrift 支持幾大類數據結構:基本類型、結構體和異常類型、容器類型、服務類型。

基本類型:

bool:布爾值 (true or false), one byte
byte:有符號字節
i16:16位有符號整型
i32:32位有符號整型
i64:64位有符號整型
double:64位浮點型
string:未知編碼或者二進制的字符串

結構體和異常類型:

Thrift 結構體 (struct) 在概念上類似于 C 語言結構體類型,在 java 中 Thrift 結構體將會被轉換成面向對象語言的類。struct 的定義如下:

struct UserDemo {1: i32 id;2: string name;3: i32 age = 25;4: string phone;
}

struct 具有以下特性:?

struct 不能繼承,但是可以嵌套,不能嵌套自己
其成員都是有明確類型
成員是被正整數編號過的,其中的編號使不能重復的,這個是為了在傳輸過程中編碼使用(詳情往下看備注1)
成員分割符可以是逗號(,)或是分號(;),而且可以混用,但是為了清晰期間,建議在定義中只使用一種,比如java學習者可以就使用逗號(;)
字段會有optional和required之分(詳情往下看備注2)
每個字段可以設置默認值
同一文件可以定義多個struct,也可以定義在不同的文件,進行include引入

備注1:數字標簽作用非常大,隨著項目開發的不斷發展,也許字段會有變化,但是建議不要輕易修改這些數字標簽,修改之后如果沒有同步客戶端和服務器端會讓一方解析出問題。

備注2:關于 struct 字段類型,規范的 struct 定義中的每個域均會使用 required 或者 optional 關鍵字進行標識,但是如果不指定則為無類型,可以不填充該值,但是在序列化傳輸的時候也會序列化進去。其中 optional 是不填充則不序列化,required 是必須填充也必須序列化。如果 required 標識的域沒有賦值,Thrift 將給予提示;如果 optional 標識的域沒有賦值,該域將不會被序列化傳輸;如果某個 optional 標識域有缺省值而用戶沒有重新賦值,則該域的值一直為缺省值;如果某個 optional 標識域有缺省值或者用戶已經重新賦值,而不設置它的 __isset 為 true,也不會被序列化傳輸。
異常在語法和功能上相當于結構體,差別是異常使用關鍵字 exception 而不是 struct 聲明。它在語義上不同于結構體:當定義一個 RPC 服務時,開發者可能需要聲明一個遠程方法拋出一個異常。

容器類型

Thrift 容器與目前流行編程語言的容器類型相對應,有3種可用容器類型:

list<t>:元素類型為t的有序表,容許元素重復。對應java的ArrayList
set<t>:元素類型為t的無序表,不容許元素重復。對應java的HashSet
map<t,t>:鍵類型為t,值類型為t的kv對,鍵不容許重復。對對應Java的HashMap

其中容器中元素類型可以是除了 service 外的任何合法 Thrift 類型(包括結構體和異常)。

服務類型

服務的定義方法在語義上等同于面向對象語言中的接口。Thrift 編譯器會產生執行這些接口的 client 和 server 存根(詳情下一節會具體描述)。下面我們就舉個簡單的例子解釋 service 如何定義:

service QuerySrv{/*** 本方法實現根據名字和年齡來找到對應的用戶信息*/UserDemo qryUser(1:string name, 2:i32 age);/*** 本方法實現根據id找到對應用戶的手機號碼*/string queryPhone(1:i32 id);
}

在上面的例子中我們定義了一個 service 類型的結構,里面包含兩個方法的定義。

在定義 services 的時候,我們還需要了解一下規則:

繼承類必須實現這些方法
參數可以是基本類型或者結構體
所有的參數都是const類型,不能作為返回值
返回值可以是void(oneway的返回值一定是void)
服務支持繼承,一個service可使用extends關鍵字繼承另一個service
服務不支持重載

除上面所提到的四大數據類型外,Thrift 還支持枚舉類型(enum)和常量類型(const)。

命名空間

Thrift 中的命名空間類似于 java 中的 package,它們提供了一種組織(隔離)代碼的簡便方式。名字空間也可以用于解決類型定義中的名字沖突。

3.4 傳輸體系

傳輸協議

Thrift 支持多種傳輸協議,用戶可以根據實際需求選擇合適的類型。Thrift 傳輸協議上總體可劃分為文本 (text) 和二進制 (binary) 傳輸協議兩大類,一般在生產環境中使用二進制類型的傳輸協議為多數(相對于文本和 JSON 具有更高的傳輸效率)。常用的協議包含:

TBinaryProtocol:是Thrift的默認協議,使用二進制編碼格式進行數據傳輸,基本上直接發送原始數據
TCompactProtocol:壓縮的、密集的數據傳輸協議,基于Variable-length quantity的zigzag 編碼格式
TJSONProtocol:以JSON (JavaScript Object Notation)數據編碼協議進行數據傳輸
TDebugProtocol:常常用以編碼人員測試,以文本的形式展現方便閱讀

關于以上幾種類型的傳輸協議,如果想更深入更具體的了解其實現及工作原理,可以參考站外相關文章《thrift源碼研究》。

傳輸方式

與傳輸協議一樣,Thrift 也支持幾種不同的傳輸方式。
1. TSocket:阻塞型 socket,用于客戶端,采用系統函數 read 和 write 進行讀寫數據。
2. TServerSocket:非阻塞型 socket,用于服務器端,accecpt 到的 socket 類型都是 TSocket(即阻塞型 socket)。
3. TBufferedTransportTFramedTransport 都是有緩存的,均繼承TBufferBase,調用下一層 TTransport 類進行讀寫操作嗎,結構極為相似。其中 TFramedTransport 以幀為傳輸單位,幀結構為:4個字節(int32_t)+傳輸字節串,頭4個字節是存儲后面字節串的長度,該字節串才是正確需要傳輸的數據,因此 TFramedTransport 每傳一幀要比 TBufferedTransport 和 TSocket 多傳4個字節。
4. TMemoryBuffer 繼承 TBufferBase,用于程序內部通信用,不涉及任何網絡I/O,可用于三種模式:(1)OBSERVE模式,不可寫數據到緩存;(2)TAKE_OWNERSHIP模式,需負責釋放緩存;(3)COPY模式,拷貝外面的內存塊到TMemoryBuffer。
5. TFileTransport 直接繼承 TTransport,用于寫數據到文件。對事件的形式寫數據,主線程負責將事件入列,寫線程將事件入列,并將事件里的數據寫入磁盤。這里面用到了兩個隊列,類型為 TFileTransportBuffer,一個用于主線程寫事件,另一個用于寫線程讀事件,這就避免了線程競爭。在讀完隊列事件后,就會進行隊列交換,由于由兩個指針指向這兩個隊列,交換只要交換指針即可。它還支持以 chunk(塊)的形式寫數據到文件。
6. TFDTransport 是非常簡單地寫數據到文件和從文件讀數據,它的 write 和 read 函數都是直接調用系統函數 write 和 read 進行寫和讀文件。
7. TSimpleFileTransport 直接繼承 TFDTransport,沒有添加任何成員函數和成員變量,不同的是構造函數的參數和在 TSimpleFileTransport 構造函數里對父類進行了初始化(打開指定文件并將fd傳給父類和設置父類的close_policy為CLOSE_ON_DESTROY)。
8. TZlibTransport 跟 TBufferedTransport 和 TFramedTransport一樣,調用下一層 TTransport 類進行讀寫操作。它采用<zlib.h>提供的 zlib 壓縮和解壓縮庫函數來進行壓解縮,寫時先壓縮再調用底層 TTransport 類發送數據,讀時先調用 TTransport 類接收數據再進行解壓,最后供上層處理。
9. TSSLSocket 繼承 TSocket,阻塞型 socket,用于客戶端。采用 openssl 的接口進行讀寫數據。checkHandshake()函數調用 SSL_set_fd 將 fd 和 ssl 綁定在一起,之后就可以通過 ssl 的 SSL_read和SSL_write 接口進行讀寫網絡數據。
10. TSSLServerSocket 繼承 TServerSocket,非阻塞型 socket, 用于服務器端。accecpt 到的 socket 類型都是 TSSLSocket 類型。
11. THttpClientTHttpServer 是基于 Http1.1 協議的繼承 Transport 類型,均繼承 THttpTransport,其中 THttpClient 用于客戶端,THttpServer 用于服務器端。兩者都調用下一層 TTransport 類進行讀寫操作,均用到TMemoryBuffer 作為讀寫緩存,只有調用 flush() 函數才會將真正調用網絡 I/O 接口發送數據。

TTransport 是所有 Transport 類的父類,為上層提供了統一的接口而且通過 TTransport 即可訪問各個子類不同實現,類似多態。

?

四. 選擇 java server 的藝術

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

其中,protocol 定義了消息是怎樣序列化的;transport 定義了消息是怎樣在客戶端和服務器端之間通信的;server 用于從 transport 接收序列化的消息,根據 protocol 反序列化之,調用用戶定義的消息處理器,并序列化消息處理器的響應,然后再將它們寫回 transport。

Thrift 模塊化的結構使得它能提供各種 server 實現。下面列出了 Java 中可用的 server 實現:

TSimpleServer
TNonblockingServer
THsHaServer
TThreadedSelectorServer
TThreadPoolServer

有多個選擇固然是很好的,但如果不清楚個中差別則是個災難。所以接下來就談談這些 server 之間的區別,并通過一些簡單的測試以說明它們的性能特點。

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;
}

?特別申明,本章節的測試結果摘自站外文章,詳情請看文末鏈接

圖 4.1

?

圖 4.2

結果正如我們想像的那樣,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 線程。

?

圖 4.3

?

圖 4.4

結果顯示,TThreadedSelectorServer 比 THsHaServer 的吞吐量高得多,并且維持在一個更低的 latency 上。

TThreadedSelectorServer vs. TThreadPoolServer

最后,還剩下 TThreadPoolServer。TThreadPoolServer 與其他三種 server 不同的是:

有一個專用的線程用來接受連接
一旦接受了一個連接,它就會被放入 ThreadPoolExecutor 中的一個 worker 線程里處理。
worker 線程被綁定到特定的客戶端連接上,直到它關閉。一旦連接關閉,該 worker 線程就又回到了線程池中。
你可以配置線程池的最小、最大線程數,默認值分別是5(最小)和 Integer.MAX_VALUE(最大)。

這意味著,如果有1萬個并發的客戶端連接,你就需要運行1萬個線程。所以它對系統資源的消耗不像其他類型的 server 一樣那么“友好”。此外,如果客戶端數量超過了線程池中的最大線程數,在有一個 worker 線程可用之前,請求將被一直阻塞在那里。

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

?

圖 4.5

?

圖 4.6

我想你可以從上面的描述可以幫你做出決定:哪一種 Thrift server 適合你。

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

?

五. Let's do it

上面已經介紹了很多理論知識了,很多同學還是不知道如何使用呢!好吧,是時候表演真正的技術了(LOL...)。

所謂大道至簡,講的就是最簡單的代碼就是最優美的代碼,只要功能強悍,最簡單的代碼也掩蓋不了它出眾的氣質。下面就來給大伙兒講講如何使用 Thrift 強大的代碼生成引擎來生成 java 代碼,并通過詳細的步驟實現 Thrift Server 和 Client 調用。

備注:本文實現基于 Thrift-0.9.2 版本,實現過程忽略日志處理等非關鍵代碼。

步驟一:首先從官網中下載對應的 Window 平臺編譯器(點擊下載 thrift-0.9.2.exe)。使用 IDL 描述語言建立 .thrift 文件。本文提供一個實現簡單功能的測試案例,如下所示:

/**
* 文件名為TestQry.thrift
* 實現功能:創建一個查詢結果struct和一個服務接口service
* 基于:thrift-0.9.2
**/
namespace java com.thrift
struct QryResult {/***返回碼, 1成功,0失敗*/1:i32 code; /***響應信息*/2:string msg;
}
service TestQry{/*** 測試查詢接口,當qryCode值為1時返回"成功"的響應信息,qryCode值為其他值時返回"失敗"的響應信息* @param qryCode測試參數*/QryResult qryTest(1:i32 qryCode)
}

步驟二:將上述 TestQry.thrift 文件與 thrift-0.9.2.exe 放在同一目錄,如下:

?

圖 5.1

在命令提示符 CMD 中進入文件目錄所在目錄,執行代碼生成命令:

thrift-0.9.2.exe -r -gen java TestQry.thrift

執行之后,我們在文件夾中可以看到生成的 java 代碼

圖 5.2

步驟三:接下來我們新建 Maven Project(注意:JDK 版本1.5及以上),將上一步驟生成的代碼拷貝到項目,并在 pom.xml 中加載 Thrift 的依賴,如下

<dependencies><dependency><groupId>org.apache.thrift</groupId><artifactId>libthrift</artifactId><version>0.9.2</version></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency>
</dependencies>

步驟四:創建 QueryImp.java 實現 TestQry.Iface 接口,關鍵代碼如下

public class QueryImp implements TestQry.Iface{@Overridepublic QryResult qryTest(int qryCode) throws TException {QryResult result = new QryResult();if(qryCode==1){result.code = 1;result.msg = "success";}else{result.code = 0;result.msg = "fail";}return result;}
}

步驟五:創建 ThriftServerDemo.java 實現服務端(本例采用非阻塞I/O,二進制傳輸協議),關鍵代碼如下

public class ThriftServerDemo {private final static int DEFAULT_PORT = 30001;private static TServer server = null;public static void main(String[] args){try {TNonblockingServerSocket socket = new TNonblockingServerSocket(DEFAULT_PORT);TestQry.Processor processor = new TestQry.Processor(new QueryImp());TNonblockingServer.Args arg = new TNonblockingServer.Args(socket);arg.protocolFactory(new TBinaryProtocol.Factory());arg.transportFactory(new TFramedTransport.Factory());arg.processorFactory(new TProcessorFactory(processor));server = new TNonblockingServer (arg);server.serve();} catch (TTransportException e) {e.printStackTrace();}}
}

步驟六:創建 ThriftClientDemo.java 實現客戶端,關鍵代碼如下

public class ThriftClientDemo {private final static int DEFAULT_QRY_CODE = 1;public static void main(String[] args){try {TTransport tTransport = getTTransport();TProtocol protocol = new TBinaryProtocol(tTransport);TestQry.Client client = new TestQry.Client(protocol);QryResult result = client.qryTest(DEFAULT_QRY_CODE);System.out.println("code="+result.code+" msg="+result.msg);}catch (Exception e) {e.printStackTrace();}}private static TTransport getTTransport() throws Exception{try{TTransport tTransport = getTTransport("127.0.0.1", 30001, 5000);if(!tTransport.isOpen()){tTransport.open();}return tTransport;}catch(Exception e){e.printStackTrace();}return null;}private static TTransport getTTransport(String host, int port, int timeout) {final TSocket tSocket = new TSocket(host, port, timeout);final TTransport transport = new TFramedTransport(tSocket);return transport;}
}

好的,所有準備工作都已經做好了,接下來我們就來進行 Client 和 Server 的通信。先運行 ThriftServerDemo 啟動 Server,然后運行 ThriftClientDemo.java 創建 Client 進行調用,當 qryCode = 1 時,結果如下

code=1 msg=success

當 qryCode = 0 時,結果如下

code=0 msg=fail

附上項目的代碼結構:

圖 5.3

你看我沒騙你吧,是不是 so easy ?

當然在項目中使用時絕對沒有這么簡單,但上面的栗子已經足夠用來指導你進行 Thrift 服務端和客戶端開發了。

?

六. 路漫漫其修遠兮

到目前為止你所看到的都不是源碼分析層面的知識,本文的目的也并非在此。掌握任何一門技術,都應該先從其宏觀體系和架構開始了解,然后再去深入研究其中的細節和精髓。如果一開始就追求所謂的源碼解析等“高大上”的東西,反而會因為擁有了一顆大樹而失去了欣賞整個森林的美妙。

當然,筆者下一步的計劃就是深入研究 Thrift 的實現,希望能和大家一起交流共同進步。

?

參考文章

[1] 《Apache Thrift - 可伸縮的跨語言服務開發框架》

[2] 《Thrift Java Servers Compared》

?

轉載于:https://www.cnblogs.com/cyfonly/p/6059374.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/394667.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394667.shtml
英文地址,請注明出處:http://en.pswp.cn/news/394667.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

instagram技術_Instagram9位科技女孩進行技術采訪的主要技巧

instagram技術by Rachel通過瑞秋 Instagram9位科技女孩進行技術采訪的主要技巧 (Top tips for technical interviews from nine of Instagram’s tech girls) My job-hunt came to an end a few weeks ago. After endless phone interviews, coding challenges, and on-sites,…

彈出框 每次打開 滾動條置頂_微信置頂文字怎么弄?微信置頂一句話教程

今日支付寶紅包支付寶首頁搜索511501453馬上領取紅包(支付寶雙十二活動&#xff0c;瓜分15億紅包)(領取后一定要記得使用&#xff0c;不然會浪費的呦&#xff0c;更會影響第二天的領取&#xff01;)奶思靚機“ 一 個 有 用 的 公 眾 號 の ”嗨&#xff0c;最近很流行在微信上面…

Python學習_字符串格式化

#!/usr/bin/env python # -*- coding:utf-8 -*-# 百分號格式化 # %[(name)[flags][width].[precision]]typecode # name : 指定占位符的key # flags : - 空格 0 # width : 寬度 # precision : 小數點后保留的位數 # typecode : 必需,數據類型 # 字符串里面有%的時候, %%表示一…

python 3 面向過程編程

python 3 面向過程編程 核心是過程&#xff08;流水線式思維&#xff09;&#xff0c;過程即解決問題的步驟&#xff0c;面向過程的設計就像設計好一條工業流水線&#xff0c;是一種機械式的思維方式。 1、優點&#xff1a;程序結構清晰&#xff0c;可以把復雜的問題簡單化&…

在ionic/cordova中使用百度地圖插件

在ionic項目中&#xff0c;如果想實現定位功能&#xff0c;可以使用ng-cordova提供的cordova-plugin-geolocation。 但由于高墻的緣故&#xff0c;國內andorid環境下&#xff0c;此插件不起作用&#xff08;ios環境下可用&#xff09;。 國內比較好的是現實使用百度地圖提供的A…

django國際化與html語言,Django 國際化

Django 國際化Django 支持國際化&#xff0c;多語言。Django的國際化是默認開啟的&#xff0c;如果您不需要國際化支持&#xff0c;那么您可以在您的設置文件中設置 USE_I18N False&#xff0c;那么Django會進行一些優化&#xff0c;不加載國際化支持機制。NOTE: 18表示Intern…

mongo 刪除節點_將生產節點/ Express Mongo App部署到AWS —反思

mongo 刪除節點在AWS中部署生產Web應用程序的經驗教訓 (Lessons learned deploying a production web application in AWS) 背景 (Background) This is not a code-based tutorial. It consists of all the things I wish I knew before I started the project and the steps I…

漢諾塔問題遞歸算法python代碼_[python]漢諾塔問題遞歸實現

一、問題描述及算法步驟 漢諾塔問題的大意是有三根柱子a, b, c&#xff0c;現在a柱有N個盤子從下往上尺寸遞減排列&#xff0c;要求&#xff1a; 1. 將a上的盤子移動到c柱上; 2. 每次移動一個盤子; 3. 柱子上的盤子始終必須是大的在下面image.png 漢諾塔問題的經典實現算法步驟…

【硬件】PCB設計步驟

前言 合理的PCB設計步驟&#xff0c;可以減少反復修改的可能性。動手設計PCB前&#xff0c;需要按步就班準備一些資料&#xff0c;即使是小項目。 本文將講解如何一次性成功地設計一款PCB的常規步驟。 當然&#xff0c;如果是一個系統&#xff0c;則需要按照瀑布式的思路&#…

linux install StarDict

1.  sudo apt-get install stardict 2.  Downloads from: http://abloz.com/huzheng/stardict-dic/zh_CN/ 3.  tar jxf stardict-21shijishuangxiangcidian-2.4.2.tar.bz2 -C /usr/share/stardict/dic (etc other dictionaries)轉載于:https://www.cnblogs.com/HurryXin/…

交付方式 saas_我在全職工作時如何交付我的第一個SaaS副項目

交付方式 saasby Tigran Hakobyan由Tigran Hakobyan 我在全職工作時如何交付我的第一個SaaS副項目 (How I shipped my first SaaS side-project while working full-time) This is my personal story of how I shipped my very first SaaS side-project while working full-ti…

nginx搭建基于http協議的視頻點播服務器

1&#xff0c;于由自己的服務器上已經安裝好nginx(具體安裝方法見我的另一篇文章&#xff0c;Linux中安裝nginx)&#xff0c;所以不再安裝。 2&#xff0c;下載nginx_mod_h264_streaming-2.2.7.tar.gz(自己在網上搜吧)。 3&#xff0c;安裝pcre&#xff0c;先看有沒有安裝。 [r…

plsql 批量調存儲過程_數控雙端開榫機:批量銑榫頭真牛氣

數控雙端開榫機主要用于實木家具批量化銑榫頭專用&#xff0c;因為其本身的優勢逐漸被家具廠老板們所接受&#xff0c;是目前家具生產不可缺少的一款自動化設備&#xff0c;給企業節約了生產成本&#xff0c;今天又焦峰小編來給大家講解一下。主要技術參數&#xff1a;知乎視頻…

c 向html頁面傳值,html頁面之間的傳值,獲取元素和方法的調用

這篇文章是自己在項目中遇到&#xff0c;同時參考了網上的資料&#xff0c;作為筆記參考使用一、頁面之間的傳值1、使用cookie傳值封裝簡單使用&#xff1a;//獲取cookiefunction getCookie(name){var arr,regnew RegExp("(^| )"name"([^;]*)(;|$)");if(ar…

Codeforces Round #364 (Div. 1) (差一個后綴自動機)

B. Connecting Universities 大意: 給定樹, 給定2*k個點, 求將2*k個點兩兩匹配, 每個匹配的貢獻為兩點的距離, 求貢獻最大值 單獨考慮每條邊$(u,v)$的貢獻即可, 最大貢獻顯然是左右兩側點的最小值. #include <iostream> #include <algorithm> #include <cstdio&…

Python黑魔法

1. 賦值 In [1]: x 1...: y 21...: print x, y...: ...: x, y y, x...: print x, y 1 21 21 1 2. 列表合并 In [2]: a1 [(2,3),(3,4)]...: a2 [(4,5)]...: a a1 a2...: print a [(2, 3), (3, 4), (4, 5)] 3. 字典合并 方式1: In [3]: d1 {a: 1}...: d2 {b: 2}...: ...…

python時間差怎么轉換為數字_pandas進行時間數據的轉換和計算時間差并提取年月日...

#pd.to_datetime函數 #讀取數據 import pandas as pd data pd.read_csv(police.csv) #將stop_date轉化為datetime的格式的dataframe&#xff0c;存到stop_datetime data[stop_datetime] pd.to_datetime(data.stop_date) #自定義一個時間&#xff0c;計算時間差 data_new pd.…

人臉識別html5效果,用HTML5實現人臉識別

注&#xff1a;今天 HTML5 小組沙龍《論道 HTML5 》分享時有朋友問到一個問題&#xff0c; getUserMedia 是否會支持人臉識別&#xff0c;我當時的答案是這應該是應用來實現的功能&#xff0c;而不是規范要完成的工作。而我之前在網上看到過一篇關于 getUserMedia 和人臉識別的…

企業如何尋找最合適的托管數據中心,以維持IT和業務的增長運營

想象一下&#xff0c;當您興奮地拿了鑰匙&#xff0c;走進您剛買的新家時&#xff0c;才突然意識到新家還沒通電&#xff0c;互聯網寬帶也還沒有通&#xff0c;而想要找個電工或者別的相關技術支持人員也不見蹤影。而且&#xff0c;更糟糕的是&#xff0c;您似乎還聽到您附近的…

gt爵士變形步驟_代碼廣播簡介:您可以編碼為24/7的爵士節拍

gt爵士變形步驟閱讀本文時&#xff0c;您可以繼續閱讀Code Radio。 (You can go ahead and start listening to Code Radio while you read this) Most developers I know listen to music while they code. When the meetings are over, the headphones come out.我認識的大多…