Apache Thrift 中篇

轉載地址:http://blog.csdn.net/yinwenjie/article/details/49685763

(接上文《架構設計:系統間通信(11)——RPC實例Apache Thrift 上篇》)

3、Aapche Thrift詳解

在《架構設計:系統間通信(10)——RPC的基本概念》一文中,我專門介紹了一款RPC規範的具體實現中哪些要素和性能息息相關。包括了RPC通訊採用的數據封裝格式、RPC通訊採用的網絡IO模型和RPC所採用的請求處理方式。這個小節我們對Apache Thrift中的這三個要素,這樣讀者就可以知曉爲什麼Apache Thrift的性能如此高效了。

3-1、Aapche Thrift與消息格式

Apache Thrift支持多種消息格式封裝。這些消息格式是如果進行編碼和解碼的是不需要使用者關心的,只需要根據自己的需要制定不同的消息封裝格式即可。Apache Thrift所有消息格式封裝的實現,都繼承與TProtocol這個抽象類,如下圖所示:

這裏寫圖片描述

3-1-1、TBinaryProtocol

二進制流的編碼格式。由於需要支持跨語言,所以Apache Thrift支持有限的幾種通用類型,包括基本類型(Float、Double、Integer、Long、String、Short)、集合類型(Map、Set、List)還有Pojo類型(實際上就是前兩者若干類型的組合形式)。

那麼這個類所生成的二進制流和傳統的java序列化後生成的二進制流有什麼樣的區別(或者是優勢)呢?我們可以通過閱讀TBinaryProtocol的源代碼進行研究。

我們以TBinaryProtocol中,對Integer的序列化過程進行詳細的解釋,來對比java提供的其他幾種序列化的方式找到不同。首先java中,如果要將一個Integer對象通過網絡發送出去,要做的第一件事情就是序列化,那麼我們常用的序列化方式有兩種,如下所示:

  • java中序列化Integer對象的第一種方法:
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">Integer integerObject = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10066329</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
integerObject<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.toString</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getBytes</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>
  • java中序列化Integer對象的第二種方法:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">ByteArrayOutputStream aStream = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ByteArrayOutputStream();
ObjectOutputStream oStream = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ObjectOutputStream(aStream);
oStream.writeObject(integerObject);
aStream.toByteArray();</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

第一種方式是將Integer對象中的值序列化;第二種方式,是將Integer整個對象序列化。這兩種方式雖然都產生byte[],實際上性質是完全不一樣的。我們來看一下這兩種方式產生的byte[]的內容:

  • 序列化Integer的值:

[49, 48, 48, 54, 54, 51, 50, 57]

  • 序列化整個Integer對象:

[-84, -19, 0, 5, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 73, 110, 116, 101, 103, 101, 114, 18, -30, -96, -92, -9, -127, -121, 56, 2, 0, 1, 73, 0, 5, 118, 97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114, -122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 0, -103, -103, -103]

第一種方式序列化後,byte數組有8個byte元素(因爲是首先轉換成字符串的,所以實際上這個大小會隨着Integer值的大小增加而增加);第二中方式序列化後,byte數組一共有 > 20 個byte元素,其中除了記錄Integer的值以外,還包括描述這個類型的其他屬性。

那麼我們再來看看TBinaryProtocol中,是如何序列化Integer類型的。首先我們來看一下TBinaryProtocol進行Integer序列化的這部分源代碼,如下圖所示:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] i32out = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>];
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">writeI32</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i32) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> TException {
    i32out[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span> & (i32 >> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">24</span>));
    i32out[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span> & (i32 >> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">16</span>));
    i32out[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span> & (i32 >> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>));
    i32out[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span> & (i32));
    trans_.write(i32out, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

計算過程可以通過下圖來表示:

這裏寫圖片描述

通過4次位計算,得到了一個長度爲4個byte數組,並且這個數組的大小並不會隨着整數大小的增加而變化。並且位運算的速度是所有計算中速度最快的一種計算。反序列化的過程相似,對這個大小爲4的byte[]數組重新進行位計算即可:

<code class="hljs vbnet has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">((buf[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">off</span>] & <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span>) << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">24</span>) |
((buf[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">off</span>+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>] & <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span>) << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">16</span>) |
((buf[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">off</span>+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>] & <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span>) <<  <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>) |
((buf[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">off</span>+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>] & <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0xff</span>));</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>

由於本文的篇幅和寫作目的所限,不能一一介紹TBinaryProtocol的各種序列化方式,但是通過對TBinaryProtocol中Integer的序列化過程,我們可以找到TBinaryProtocol處理過程的優勢,包括速度和大小的優勢。所以,如果您的使用環境對序列化過程沒有特別的要求(例如後面要提到的大量的負數情況),那麼直接使用TBinaryProtocol進行數據格式的封裝的就可以了。

byte是一個8位二進制描述(一個字節),在java中,一個int需要4個byte進行表示,而。“0x”的前綴表示16進制數字,那麼0xff的二進制表示就是 1111 1111;“&”是“與”運算符,這個運算符用於二進制計算,1 & 1 = 1,其餘情況都 = 0;“<<” 表示左移運算,0011 << 2 = 1100;”>>”表示右移運算,1100 >> 2 = 0011;

3-1-2、TCompactProtocol

使用zigzag編碼方式緊湊傳輸協議。zigzag編碼的優勢在於記錄數字類型(整數、單精度浮點和雙精度浮點),最特別的是zigzag編碼對負數的記錄。在計算機中,都會使用很大的數字表示負數,爲了保證節約傳輸量,zigzag編碼採用正數與負數交錯的方式,把負數轉換爲一個正數進行記錄。下面我們具體來分析一下TCompactProtocol中對32位整數的序列化方式,以下是TCompactProtocol中對32爲整數的處理代碼:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * Write an i32 as a zigzag varint.
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">writeI32</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i32) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> TException {
    writeVarint32(intToZigZag(i32));
}

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * Convert n into a zigzag int. This allows negative numbers to be
 * represented compactly as a varint.
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">intToZigZag</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> n) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (n << <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>) ^ (n >> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">31</span>);
}

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * Write an i32 as a varint. Results in 1-5 bytes on the wire.
 * TODO: make a permanent buffer like writeVarint64?
 */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] i32buf = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>];
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">writeVarint32</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> n) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> TException {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> idx = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">while</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((n & ~<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0x7F</span>) == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) {
            i32buf[idx++] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)n;
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// writeByteDirect((byte)n);</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>;
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
            i32buf[idx++] = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>)((n & <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0x7F</span>) | <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0x80</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// writeByteDirect((byte)((n & 0x7F) | 0x80));</span>
            n >>>= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7</span>;
        }
    }
    trans_.write(i32buf, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, idx);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li></ul>

以上代碼片段一共有一個對外的調用方法,和兩個分別名爲intToZigZag和writeVarint32的私有方法。從字面上的意義我們可以知道:當對一個32位整數進行編碼時,首先將這個32位整數轉成ZigZag編碼格式,然後在序列化爲“變長的32位整數”。那麼這個處理的具體過程是什麼樣的呢?我們以一個較大的32位整數(161061273,二進制計數爲:1001100110011001100110011001)爲例,進行講解:

  • 首先將整個這個整數做成ZigZag編碼格式: 
    這裏寫圖片描述

  • 然後進行“變長”處理:

這裏寫圖片描述

  • 可以看到,上面的“變長”計算一共進行了5次,比TBinaryProtocol中的32位整數序列化還要多出一個byte。這是爲什麼呢?因爲這個數字比較長。

  • 但實際處理中,我們一般使用的數據都是比較小的。這也是爲什麼首先要使用ZigZag編碼把某個負數的符號位從高位移動到低位的原因。實際上,在實際過程中,變長計算一般只會進行二至三次就完成。這樣,在大多數情況下,完成一個32位整數的序列化,TCompactProtocol做使用的空間就比TBinaryProtocol要小。

  • 那麼經過分析,對於TCompactProtocol和TBinaryProtocol的選擇的經驗是:如果傳輸的信息中,基本都是字符串,那麼使用TCompactProtocol還是使用TBinaryProtocol基本上都是差不多的;如果需要傳輸的信息中,會有較多的“低位數字”,那麼建議使用TCompactProtocol。

3-1-3、其他傳輸格式封裝:

當然Apache Thrift還提供其他的傳輸格式封裝。不同的需求場景下,您可以使用根據需要選用這些信息傳輸格式:

這裏寫圖片描述

3-2、Aapche Thrift與通信模型

Apache Thrift支持阻塞式同步IO通訊模型和非阻塞式異步IO通信模型。這裏說明一下,我在這個系列的文章中,已經詳細講述了各種IO模型的特點和工作原理(請參見我另外幾篇文章《架構設計:系統間通信(3)——IO通信模型和JAVA實踐 上篇》、《架構設計:系統間通信(4)——IO通信模型和JAVA實踐 中篇》、《架構設計:系統間通信(5)——IO通信模型和JAVA實踐 下篇》)。所以讀者您如果度過本人的拙作,那麼您一定清楚,要發揮Apache Thrift性能上的優勢,那麼一定要在正式生產環境中採用Apache Thrift對非阻塞式異步IO通信模型的支持。下面的代碼我們將向您展示Apache Thrift的這種特性:

在給出示例代碼之前一定要再強調一次,Apache Thrift的服務器端和客戶端一定要採用相同的通信模型。這就是說如果Apache Thrift的服務器端採用的是非阻塞異步通信模型,那麼Apache Thrift客戶端也一定要採用非阻塞異步通信模型,否則就無法通信。

  • 服務器的非阻塞異步通信代碼:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> testThrift.man;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.nio.channels.Selector;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.util.concurrent.Executors;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.commons.logging.Log;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.commons.logging.LogFactory;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.log4j.BasicConfigurator;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.TProcessor;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.protocol.TBinaryProtocol;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.server.THsHaServer;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.transport.TNonblockingServerSocket;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.HelloWorldService;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.HelloWorldService.Iface;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.impl.HelloWorldServiceImpl;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">HelloNonServerDemo</span> {</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> {
        BasicConfigurator.configure();
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 日誌
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Log LOGGER = LogFactory.getLog(HelloNonServerDemo.class);

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> SERVER_PORT = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8090</span>;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">startServer</span>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// log4j日誌,如果您工程裏面沒有加入log4j的支持,請待用system.out</span>
            HelloNonServerDemo.LOGGER.info(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"HelloWorld TSimpleServer start ...."</span>);

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 服務執行控制器(告訴apache thrift,實現了HelloWorldService.Iface接口的是具體的哪一個類)</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// HelloWorldServiceImpl類的代碼,就不在贅述了,無論採用哪種通信模型,它的代碼都不會變化</span>
            TProcessor tprocessor = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HelloWorldService.Processor<Iface>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HelloWorldServiceImpl());

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 非阻塞異步通訊模型(服務器端)</span>
            TNonblockingServerSocket serverTransport = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TNonblockingServerSocket(HelloNonServerDemo.SERVER_PORT);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Selector這個類,是不是很熟悉。</span>
            serverTransport.registerSelector(Selector.open());

            THsHaServer.Args tArgs = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> THsHaServer.Args(serverTransport);
            tArgs.processor(tprocessor);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 指定消息的封裝格式(採用二進制流封裝)</span>
            tArgs.protocolFactory(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TBinaryProtocol.Factory());
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 指定處理器的所使用的線程池。</span>
            tArgs.executorService(Executors.newFixedThreadPool(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>));

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 啓動服務</span>
            THsHaServer server = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> THsHaServer(tArgs);
            server.serve();
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
            HelloNonServerDemo.LOGGER.error(e);
        }
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> args
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) {
        HelloNonServerDemo server = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HelloNonServerDemo();
        server.startServer();
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li></ul>
  • 客戶端的非阻塞異步通信代碼:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> testThrift.client;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.commons.logging.Log;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.commons.logging.LogFactory;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.log4j.BasicConfigurator;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.TException;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.async.AsyncMethodCallback;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.async.TAsyncClientManager;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.protocol.TBinaryProtocol;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> org.apache.thrift.transport.TNonblockingSocket;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.HelloWorldService;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.Reponse;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.HelloWorldService.AsyncClient;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.HelloWorldService.AsyncClient.send_call;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> testThrift.iface.Request;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">HelloNonClient</span> {</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> {
        BasicConfigurator.configure();
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 日誌
     */</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Log LOGGER = LogFactory.getLog(HelloNonClient.class);

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Object WAITOBJECT = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Object();

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> Exception {
        TNonblockingSocket transport = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TNonblockingSocket(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"127.0.0.1"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8090</span>);
        TAsyncClientManager clientManager = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TAsyncClientManager();

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 準備調用參數(這個testThrift.iface.Request,是我們通過IDL定義,並且生成的)</span>
        Request request = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Request(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"{\"param\":\"field1\"}"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\\mySerivce\\queryService"</span>);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 這是客戶端對非阻塞異步網絡通信方式的支持。</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 注意使用的消息封裝格式,一定要和服務器端使用的一致</span>
        HelloWorldService.AsyncClient asyncClient = 
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> HelloWorldService.AsyncClient.Factory(clientManager, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> TBinaryProtocol.Factory()).getAsyncClient(transport);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 既然是非阻塞異步模式,所以客戶端一定是通過“事件回調”方式,接收到服務器的響應通知的</span>
        asyncClient.send(request,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span>  AsyncMethodCallback<AsyncClient.send_call>() {
            <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
             * 當服務器正確響應了客戶端的請求後,這個事件被觸發
             */</span>
            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onComplete</span>(send_call call) {
                Reponse response = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
                    response = call.getResult();
                } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (TException e) { 
                    HelloNonClient.LOGGER.error(e);
                    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
                } 
                HelloNonClient.LOGGER.info(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"response = "</span> + response);
            }

            <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
             * 當服務器沒有正確響應了客戶端的請求,或者其中過程中出現了不可控制的情況。
             * 那麼這個事件會被觸發
             */</span>
            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onError</span>(Exception exception) {
                HelloNonClient.LOGGER.info(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"exception = "</span> + exception);
            }
        });

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//這段代碼保證客戶端在得到服務器回覆前,應用程序本身不會終止</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span> (HelloNonClient.WAITOBJECT) {
            HelloNonClient.WAITOBJECT.wait();
        }
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li></ul>

以上代碼是可以直接工作的。讀者可以直接在自己的工程中執行。運行的結果和Apache Thrift上一節中Apache Thrift阻塞模式下的運行結果是一致的,只是運行過程不一樣。目前各種主流的RPC框架基本都支持非阻塞式異步IO網絡通信,如果您有興趣進行這些RPC框架的性能比較,一定要在相同的IO通信模型下進行。

3-3、Aapche Thrift與線程池

在之前的文章(《架構設計:系統間通信(10)——RPC的基本概念》),我們已經提到影響一款RPC框架性能的主要指標。除了RPC框架實現的數據封裝格式、RPC框架支持的網絡通信模型外,還有一個重要的指標就是它如何執行客戶端的請求。

在Apache Thrift中,它使用線程池技術運行具體的接口實現,響應客戶端請求(無論Apahce Thrift使用哪種數據封裝格式、使用哪種網絡通信模型)。

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">org<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.apache</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.thrift</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.server</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.THsHaServer</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Args</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.executorService</span>(ExecutorService executorService)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

可以看到,實際上Apache Thrift中設置線程池的方法,所要求的參數類型是java.util.concurrent.ExecutorService接口,也就是說只要實現了ExecutorService接口的類都可以被傳入。一般我們常使用的是java.util.concurrent.ThreadPoolExecutor這個類。

4、下文預告

在本篇文章中,我們詳細描述了Apache Thrift中和性能息息相關的三個要素:數據封裝格式的實現、網絡IO模型的支持 和 處理客戶端請求的方式。正式有這些實現的細節,才使Apache Thrift成爲一款主流的RPC框架。那麼我們在正式生產環境中,應該如何使用RPC框架才科學呢?在下文中,我們將結合RPC的特點和我自己的工作經歷,向各位讀者進行介紹。

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