Thrift之Protocol源碼分析

字號:


    之前寫過兩篇關(guān)于 Thrift 的相關(guān)文章。
    Thrift源碼剖析
    Thrift異步IO服務(wù)器源碼分析
    也算是對Thrift比較熟悉,不過對 Thrift 里面的 Protocol 部分還是黑盒使用。 雖然大概能猜到具體實(shí)現(xiàn)方式,但是還是忍不住花了一點(diǎn)點(diǎn)時(shí)間把具體代碼實(shí)現(xiàn)翻出來看看。 主要是為了滿足一下好奇心。
    簡單搞了一個(gè)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其實(shí)就是干RPC的活,所以看源碼就按著RPC遠(yuǎn)程調(diào)用的順序來看就行。
    從Hello函數(shù)調(diào)用開始,InsightClient::Hello 可以看出, 在每次RPC調(diào)用的時(shí)候,會(huì)先將函數(shù)名通過writeMessageBegin("Hello", ::apache::thrift::protocol::T_CALL, cseqid) 先發(fā)送過去。 這個(gè)過程的序列化協(xié)議很簡單,直接就是傳輸?shù)暮瘮?shù)名字符串。 然后再發(fā)送參數(shù)。 發(fā)送參數(shù)的時(shí)候,會(huì)將所有參數(shù)作為一個(gè) struct 發(fā)送 InsightHellopargs,
    所以協(xié)議的序列化過程主要都是體現(xiàn)在 struct 的序列化上面。 比如像Hi函數(shù)的參數(shù)序列化過程:
    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;
    }
    整個(gè)對象的序列化過程主要是依賴了接口 TProtocol 的函數(shù)。
    對于實(shí)現(xiàn) TProtocol 接口的序列化實(shí)現(xiàn)主要是以下三種(在thrift-0.9.0/lib/cpp/src/thrift/protocol里):
    TBinaryProtocol
    TCompactProtocol
    TJSONProtocol
    要了解協(xié)議序列化過程主要看一下 TBinaryProtocol 和 TCompactProtocol 就夠了。
    主要是如下幾個(gè)關(guān)鍵點(diǎn):
    其實(shí) writeStructStruct 和 writeStructEnd 啥屁事也不用做。
    其實(shí) writeFieldBegin 只有后兩個(gè)參數(shù)有用,第二個(gè)參數(shù)是類型,第三個(gè)參數(shù)是ID, 因?yàn)楣饪窟@兩者就可以在反序列化(讀取解析)的時(shí)候知道是哪個(gè)成員了。
    struct write 的過程其實(shí)是個(gè)遞歸的過程,也就是在write函數(shù)中, 會(huì)遞歸的調(diào)用結(jié)構(gòu)體本身每個(gè)成員的write函數(shù)。
    TCompactProtocol 和 TBinaryProtocol 的區(qū)別主要是, TCompactProtocol 對整數(shù)類型使用了 ZigZag 壓縮算法,比如 i32 類型的整數(shù)本來是4個(gè)字節(jié), 可以壓縮成 1~5 字節(jié)不等。而 i64類型的整數(shù)本來是8個(gè)字節(jié)??梢詨嚎s成 1~10 字節(jié)不等。