天天看點

Thrift之Protocol源碼分析



之前寫過兩篇關于 Thrift 的相關文章。

也算是對Thrift比較熟悉,不過對 Thrift 裡面的 Protocol 部分還是黑盒使用。 雖然大概能猜到具體實作方式,但是還是忍不住花了一點點時間把具體代碼實作翻出來看看。 主要是為了滿足一下好奇心。

簡單搞了一個Thrift的描述檔案

Insight.thrift

作為例子。

struct Person {
    1: string name,
    2: i32 age,
    3: optional string address,
}

service Insight {
    Person Hello(1: Person person),
    Person Hi(1: Person p1, 2: Person p2),
}
           

然後通過 畢竟Thrift其實就是幹RPC的活,是以看源碼就按着RPC遠端調用的順序來看就行。

從Hello函數調用開始,

InsightClient::Hello

可以看出, 在每次RPC調用的時候,會先将函數名通過

writeMessageBegin(“Hello”,

::apache::thrift::protocol::T_CALL, cseqid)

先發送過去。 這個過程的序列化協定很簡單,直接就是傳輸的函數名字元串。 然後再發送參數。 發送參數的時候,會将所有參數作為一個 struct 發送

Insight_Hello_pargs

是以協定的序列化過程主要都是展現在 struct 的序列化上面。 比如像Hi函數的參數序列化過程:

uint32_t Insight_Hi_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const {
  uint32_t xfer = 0;
  xfer += oprot->writeStructBegin("Insight_Hi_pargs");

  xfer += oprot->writeFieldBegin("p1", ::apache::thrift::protocol::T_STRUCT, 1);
  xfer += (*(this->p1)).write(oprot);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldBegin("p2", ::apache::thrift::protocol::T_STRUCT, 2);
  xfer += (*(this->p2)).write(oprot);
  xfer += oprot->writeFieldEnd();

  xfer += oprot->writeFieldStop();
  xfer += oprot->writeStructEnd();
  return xfer;
}
           

整個對象的序列化過程主要是依賴了接口 TProtocol 的函數。

對于實作 TProtocol 接口的序列化實作主要是以下三種(在

thrift-0.9.0/lib/cpp/src/thrift/protocol

裡):

  • TBinaryProtocol
  • TCompactProtocol
  • TJSONProtocol

要了解協定序列化過程主要看一下 TBinaryProtocol 和 TCompactProtocol 就夠了。

主要是如下幾個關鍵點:

  • 其實 writeStructStruct 和 writeStructEnd 啥屁事也不用做。
  • 其實 writeFieldBegin 隻有後兩個參數有用,第二個參數是類型,第三個參數是ID, 因為光靠這兩者就可以在反序列化(讀取解析)的時候知道是哪個成員了。
  • struct write 的過程其實是個遞歸的過程,也就是在write函數中, 會遞歸的調用結構體本身每個成員的write函數。
  • TCompactProtocol 和 TBinaryProtocol 的差別主要是, TCompactProtocol 對整數類型使用了 ZigZag 壓縮算法,比如 i32 類型的整數本來是4個位元組, 可以壓縮成 1~5 位元組不等。而 i64類型的整數本來是8個位元組。可以壓縮成 1~10 位元組不等。
http://yanyiwu.com/work/2015/04/05/thrift-protocol-insight.html