Thrift原始碼分析(二) 協議和編譯碼

2021-06-25 21:33:18 字數 4654 閱讀 6387

協議和編譯碼是乙個網路應用程式的核心問題之一,客戶端和伺服器通過約定的協議來傳輸訊息(資料),通過特定的格式來編譯碼字節流,並轉化成業務訊息,提供給上層框架呼叫。

thrift的協議比較簡單,它把協議和編譯碼整合在了一起。抽象類tprotocol定義了協議和編譯碼的頂層介面。個人感覺採用抽象類而不是介面的方式來定義頂層介面並不好,tprotocol關聯了乙個ttransport傳輸物件,而不是提供乙個類似gettransport()的介面,導致抽象類的擴充套件性比介面差。

tprotocol主要做了兩個事情:

1. 關聯ttransport物件

2.定義一系列讀寫訊息的編譯碼介面,包括兩類,一類是複雜資料結構比如readmessagebegin, readmessageend,  writemessagebegin, writmessageend.還有一類是基本資料結構,比如readi32, writei32, readstring, writestring

public abstract class tprotocol 

/*** scheme accessor

*/public class extends ischeme> getscheme()

}

所謂協議就是客戶端和伺服器端約定傳輸什麼資料,如何解析傳輸的資料。對於乙個rpc呼叫的協議來說,要傳輸的資料主要有:

呼叫方1. 方法的名稱,包括類的名稱和方法的名稱

2. 方法的引數,包括型別和引數值

3.一些附加的資料,比如附件,超時事件,自定義的控制資訊等等

返回方1. 呼叫的返回碼

2. 返回值

3.異常資訊

從tprotocol的定義我們可以看出thrift的協議約定如下事情:

1.先writemessagebegin表示開始傳輸訊息了,寫訊息頭。message裡面定義了方法名,呼叫的型別,版本號,訊息seqid

2.接下來是寫方法的引數,實際就是寫訊息體。如果引數是乙個類,就writestructbegin

3. 接下來寫字段,writefieldbegin, 這個方法會寫接下來的字段的資料型別和順序號。這個順序號是thrfit對要傳輸的字段的乙個編碼,從1開始

4. 如果是乙個集合就writelistbegin/writemapbegin,如果是乙個基本資料型別,比如int, 就直接writei32

5. 每個複雜資料型別寫完都呼叫write***end,直到writemessageend結束

6. 讀訊息時根據資料型別讀取相應的長度

每個write***都是採用訊息頭+訊息體的方式。我們來看tbinaryprotocol的實現。

1.writemessgebegin方法寫了訊息頭,包括4位元組的版本號和型別資訊,字串型別的方法名,4位元組的序列號seqid

2. writefieldbegin,寫了1個位元組的字段資料型別,和2個位元組欄位的順序號

3. writei32,寫了4個位元組的位元組陣列

4. writestring,先寫4位元組訊息頭表示字串長度,再寫字串位元組

5. writebinary,先寫4位元組訊息頭表示位元組陣列長度,再寫位元組陣列內容

6.readmessagebegin時,先讀4位元組版本和型別資訊,再讀字串,再讀4位元組序列號

7.readfieldbegin,先讀1個位元組的字段資料型別,再讀2個位元組的字段順序號

8. readstring時,先讀4位元組字串長度,再讀字串內容。字串統一採用utf-8編碼

public void writemessagebegin(tmessage message) throws texception  else 

}public void writefieldbegin(tfield field) throws texception

private byte i32out = new byte[4];

public void writei32(int i32) throws texception

public void writestring(string str) throws texception catch (unsupportedencodingexception uex)

}public void writebinary(bytebuffer bin) throws texception

public tmessage readmessagebegin() throws texception

return new tmessage(readstring(), (byte)(size & 0x000000ff), readi32());

} else

return new tmessage(readstringbody(size), readbyte(), readi32());

}}public tfield readfieldbegin() throws texception

public string readstring() throws texception catch (unsupportedencodingexception e)

}return readstringbody(size);

}

tprotocol定義了基本的協議資訊,包括傳輸什麼資料,如何解析傳輸的資料的基本方法。

還存在乙個問題,就是伺服器端如何知道客戶端傳送過來的資料是怎麼組合的,比如第乙個欄位是字串型別,第二個欄位是int。這個資訊是在idl生成客戶端時生成的**時提供了。thrift生成的客戶端**提供了讀寫引數的方法,這兩個方式是一一對應的,包括欄位的序號,型別等等。客戶端使用寫引數的方法,伺服器端使用讀引數的方法。

關於idl生成的客戶端**會在後面的文章具體描述。下面簡單看一下自動生成的**

1. 方法的呼叫從writemessagebegin開始,傳送了訊息頭資訊

2. 寫方法的引數,也就是寫訊息體。方法引數由乙個統一的介面tbase描述,提供了read和write的統一介面。自動生成的**提供了read, write方法引數的具體實現

3. 寫完結束 

public void write_args(org.apache.thrift.protocol.tprotocol prot) throws org.apache.thrift.texception 

public inte***ce tbase, f extends tfieldidenum> extends comparable,  serializable

public string identity; // required

public long uid; // required

public string sid; // required

public int type; // required

public string message; // required

public mapparams; // required

/** the set of fields this struct contains, along with convenience methods for finding and manipulating them. */

public enum _fields implements org.apache.thrift.tfieldidenum

oprot.writefieldbegin(uid_field_desc);

oprot.writei64(struct.uid);

oprot.writefieldend();

if (struct.sid != null)

oprot.writefieldbegin(type_field_desc);

oprot.writei32(struct.type);

oprot.writefieldend();

if (struct.message != null)

}//  自動生成的讀方法引數的方法,按照字段順序讀,給伺服器端**使用

public void read(org.apache.thrift.protocol.tprotocol iprot, handle_args struct) throws org.apache.thrift.texception

switch (schemefield.id) else

break;

case 2: // uid

if (schemefield.type == org.apache.thrift.protocol.ttype.i64) else

break;

case 3: // sid

if (schemefield.type == org.apache.thrift.protocol.ttype.string) else

break;

case 4: // type

if (schemefield.type == org.apache.thrift.protocol.ttype.i32) else

break;

Thrift學習(二)協議架構

黃色部分是使用者實現的業務邏輯 褐色部分是根據 thrift 定義的服務介面描述檔案生成的客戶端和伺服器端 框架 紅色部分是根據 thrift 檔案生成 實現資料的讀寫操作 紅色部分以下是 thrift 的傳輸體系 協議以及底層 i o 通訊,使用 thrift 可以很方便的自定義乙個服務 傳輸協議...

二 LinkedList原始碼分析

二 linkedlist原始碼分析 上篇看了下arraylist原始碼,這篇記錄下linkedlist原始碼分析 linkedlist 繼承 abstractsequentiallist 提供了對元素順序訪問的抽象 linkedlist 實現 deque介面,底層構成雙向鍊錶結構 linkedlis...

Spring原始碼分析(二)

針對spring中bean初始化時擴充套件類執行順序進行分析 通過getbean逐層最終到bean例項化前後呼叫的方法,主要是aware介面 beanpostprocessor initializingbean init method的呼叫順序 abstractbeanfactory dogetbe...