Thrift之TProtocol類體系原理及源碼詳細(xì)解析之類繼承架構(gòu)分析
我的新浪微博:http://weibo.com/freshairbrucewoo。
歡迎大家相互交流,共同提高技術(shù)。
這部分相關(guān)的類主要實(shí)現(xiàn)與協(xié)議相關(guān)的內(nèi)容,這里說的協(xié)議是指對數(shù)據(jù)傳輸格式封裝的協(xié)議,實(shí)現(xiàn)不同的協(xié)議來適合不同場景下的數(shù)據(jù)傳輸,因?yàn)樵诓煌膱鼍跋虏煌瑓f(xié)議對于數(shù)據(jù)傳輸來說效率有很大的差別。下面是這個(gè)部分相關(guān)類的類關(guān)系圖:

由以上類圖可以發(fā)現(xiàn)所有的協(xié)議類都從TProtocol類直接或間接繼承,每一個(gè)協(xié)議類都有一個(gè)對應(yīng)的生產(chǎn)對象工廠(協(xié)議工廠)。TProtocol是一個(gè)抽象的類,不能直接使用的,它有一個(gè)直接子類默認(rèn)實(shí)現(xiàn)了所有方法(空實(shí)現(xiàn)),如果我們需要定義自己的數(shù)據(jù)傳輸協(xié)議可以直接從這個(gè)類繼承。
第一節(jié) 類繼承架構(gòu)分析
為什么需要對這部分的類繼承架構(gòu)進(jìn)行分析了?上面不是有很清楚的類繼承關(guān)系圖了嗎?但是Facebook在實(shí)現(xiàn)時(shí)并不是簡單的這樣繼承下來就可以了,Facebook為了后期協(xié)議的可擴(kuò)展性和允許其他組織、團(tuán)隊(duì)或個(gè)人實(shí)現(xiàn)自己的數(shù)據(jù)傳輸(主要是數(shù)據(jù)格式的封裝)協(xié)議,里面多加了一層繼承關(guān)系,就是類圖中的TVirtualProtocol類,從類的名稱可以看出這是一個(gè)虛的協(xié)議。怎樣理解這個(gè)虛的協(xié)議了?通過閱讀代碼我覺得可以這樣理解:因?yàn)樗x為一個(gè)模板類,這個(gè)模板類有兩個(gè)參數(shù),一個(gè)用于數(shù)據(jù)傳輸?shù)恼嬲齾f(xié)議,一個(gè)是用來繼承的,它本身沒有對協(xié)議具體內(nèi)容做實(shí)現(xiàn),所以說它是一個(gè)虛的協(xié)議類。下面我們對這個(gè)類繼承架構(gòu)結(jié)合代碼實(shí)現(xiàn)來具體分析。
1 抽象類TProtocol和默認(rèn)實(shí)現(xiàn)類TProtocolDefaults
抽象類對于每一種數(shù)據(jù)類型都提供了讀寫的開始和介紹的方法,這里讀寫方法應(yīng)該是針對網(wǎng)絡(luò)IO讀寫,不過真正實(shí)現(xiàn)網(wǎng)絡(luò)讀寫還不是這里的方法,這里方法主要處理數(shù)據(jù),例如對數(shù)據(jù)格式做調(diào)整。真正實(shí)現(xiàn)網(wǎng)絡(luò)IO讀寫是下一章介紹的TTransport相關(guān)類實(shí)現(xiàn)的,那里還會(huì)對傳輸?shù)姆绞阶鱿鄳?yīng)控制,例如是否壓縮。
除了具體的數(shù)據(jù)類型有寫入和讀取的方法,消息也是需要通過網(wǎng)絡(luò)傳遞,所以也定義了消息的傳輸讀寫方法。當(dāng)然還定義了一些公用的功能,如跳過某一個(gè)結(jié)構(gòu)不讀、大小端數(shù)據(jù)格式調(diào)整、主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序的相互轉(zhuǎn)換等。
(1)首先定義純虛函數(shù):
1 virtual uint32_t writeMessageBegin_virt(const std::string& name, 2 3 const TMessageType messageType, const int32_t seqid) = 0; 4 5 virtual uint32_t writeMessageEnd_virt() = 0; 6 7 virtual uint32_t writeStructBegin_virt(const char* name) = 0; 8 9 virtual uint32_t writeStructEnd_virt() = 0;
(2)然后定義調(diào)用相應(yīng)純虛函數(shù)的函數(shù):
1 uint32_t writeMessageBegin(const std::string& name, const TMessageType messageType, const int32_t seqid) { 2 3 T_VIRTUAL_CALL();//打印調(diào)用日志函數(shù) 4 5 return writeMessageBegin_virt(name, messageType, seqid); 6 7 } 8 9 uint32_t writeMessageEnd() { 10 11 T_VIRTUAL_CALL(); 12 13 return writeMessageEnd_virt(); 14 15 } 16 17 uint32_t writeStructBegin(const char* name) {//寫結(jié)構(gòu)體開始 18 19 T_VIRTUAL_CALL(); 20 21 return writeStructBegin_virt(name); 22 23 } 24 25 uint32_t writeStructEnd() {//寫結(jié)構(gòu)體結(jié)束 26 27 T_VIRTUAL_CALL(); 28 29 return writeStructEnd_virt(); 30 31 }
(3)其他公共函數(shù)
1 uint32_t skip(TType type) { 2 3 T_VIRTUAL_CALL(); 4 5 return skip_virt(type);//跳過數(shù)據(jù)類型 6 7 } 8 9 virtual uint32_t skip_virt(TType type) { 10 11 return ::apache::thrift::protocol::skip(*this, type);// 調(diào)用此命名空間下全局函數(shù)實(shí)現(xiàn) 12 13 }
(4)全局?jǐn)?shù)據(jù)結(jié)構(gòu)定義
1 enum TType {//Thrift協(xié)議支持的數(shù)據(jù)類型枚舉定義 2 3 T_STOP = 0, 4 5 T_VOID = 1, 6 7 T_BOOL = 2, 8 9 T_BYTE = 3, 10 11 T_I08 = 3, 12 13 T_I16 = 6, 14 15 T_I32 = 8, 16 17 T_U64 = 9, 18 19 T_I64 = 10, 20 21 T_DOUBLE = 4, 22 23 T_STRING = 11, 24 25 T_UTF7 = 11, 26 27 T_STRUCT = 12, 28 29 T_MAP = 13, 30 31 T_SET = 14, 32 33 T_LIST = 15, 34 35 T_UTF8 = 16, 36 37 T_UTF16 = 17 38 39 }; 40 41 enum TMessageType {//thrift支持的消息類型 42 43 T_CALL = 1, 44 45 T_REPLY = 2, 46 47 T_EXCEPTION = 3, 48 49 T_ONEWAY = 4//函數(shù)的異步調(diào)用方式 50 51 };
還定義了一個(gè)對應(yīng)抽象工廠類,用于上產(chǎn)具體的協(xié)議對象,這就是設(shè)計(jì)模式中最常用的抽象工廠設(shè)計(jì)模式。上面代碼只是簡單列舉了兩個(gè)寫入的函數(shù),還有其他很多相關(guān)數(shù)據(jù)類型的寫入和讀取函數(shù)因?yàn)槎枷嗤袷蕉x就沒有具體拿出來說明了。
至于默認(rèn)實(shí)現(xiàn)類TProtocolDefaults主要重寫了抽象類TProtocol的非虛擬化的方法,這些方法都拋出一個(gè)方法為實(shí)現(xiàn)(TProtocolException::NOT_IMPLEMENTED)的異常。這樣做的主要目的為了下面要講到的類TVirtualProtocol提供默認(rèn)的繼承基類,從而防止無限遞歸調(diào)用。下面分析TVirtualProtocol類時(shí)具體分析如果沒有這個(gè)默認(rèn)實(shí)現(xiàn)類怎樣產(chǎn)生無限遞歸調(diào)用。
2 虛協(xié)議類TVirtualProtocol
首先看看這個(gè)特殊的模板類是怎樣定義的:
1 template <class Protocol_, class Super_=TProtocolDefaults> 2 3 class TVirtualProtocol : public Super_ { 4 5 ……//省略具體內(nèi)容 6 7 }
說它特殊我覺得主要是基于兩個(gè)方面:
(1)繼承的類可以通過模板參數(shù)傳遞,也就是說你現(xiàn)在沒有辦法肯定它現(xiàn)在帶地繼承什么類,只是默認(rèn)參數(shù)值是我們上面介紹的抽象類的默認(rèn)實(shí)現(xiàn)類。
(2)類里面的方法實(shí)現(xiàn)全部是采用把this指針轉(zhuǎn)換為第一個(gè)模板參數(shù)的類型然后調(diào)用模板參數(shù)類型的相關(guān)方法。如下面的一個(gè)方法的定義:
1 virtual uint32_t writeMessageBegin_virt(const std::string& name, 2 3 const TMessageType messageType, const int32_t seqid) { 4 5 return static_cast<Protocol_*>(this)->writeMessageBegin(name, messageType, seqid); 6 7 }//實(shí)現(xiàn)抽象類的writeMessageBegin_virt方法
由上面兩個(gè)特點(diǎn)可以發(fā)現(xiàn)這個(gè)類只是提供一個(gè)空的架構(gòu)殼,繼承的類可以指定,當(dāng)然也可以采用默認(rèn)的,后面分析具體協(xié)議實(shí)現(xiàn)的時(shí)候可以發(fā)現(xiàn)大多數(shù)協(xié)議也確實(shí)是采用默認(rèn)的繼承類,且大多數(shù)繼承這個(gè)虛協(xié)議的類都是傳遞第一個(gè)參數(shù)為自身,也就是調(diào)用自己定義的相應(yīng)函數(shù)。如TBinaryProtocolT類的定義,如下:
1 template <class Transport_> 2 3 class TBinaryProtocolT : public TVirtualProtocol< TBinaryProtocolT<Transport_> > { 4 5 ……. 6 7 }
下面來分析如果不從定義的默認(rèn)實(shí)現(xiàn)類繼承,直接從抽象類繼承怎樣會(huì)產(chǎn)生無限遞歸調(diào)用。現(xiàn)在我們假設(shè)直接從抽象類繼承,那么如果一個(gè)指向子類對象的父類(TProtocol)調(diào)用writeMessageBegin方法,因?yàn)檫@個(gè)方法不是虛擬函數(shù)(不會(huì)動(dòng)態(tài)綁定)所以就會(huì)調(diào)用父類的writeMessageBegin方法,然后父類會(huì)直接調(diào)用它的純虛函數(shù)writeMessageBegin_virt,這個(gè)函數(shù)就會(huì)動(dòng)態(tài)綁定,就會(huì)執(zhí)行子類的實(shí)現(xiàn),在這里就是通過虛協(xié)議類TVirtualProtocol實(shí)現(xiàn)的,而這個(gè)函數(shù)又會(huì)調(diào)用之類的writeMessageBegin方法,如果子類沒有實(shí)現(xiàn)這個(gè)方法,那么就又回到父類的這個(gè)方法了,從而產(chǎn)生無限遞歸調(diào)用。那么如果默認(rèn)是從默認(rèn)實(shí)現(xiàn)類TProtocolDefaults繼承,那么就會(huì)執(zhí)行它的writeMessageBegin方法,從而拋出一個(gè)異常,就不會(huì)產(chǎn)生無限遞歸調(diào)用。
3 具體協(xié)議類或?qū)崿F(xiàn)自己的協(xié)議類
這個(gè)就是類繼承架構(gòu)的最后一部分,也就是實(shí)現(xiàn)具體的數(shù)據(jù)傳輸協(xié)議。例如二進(jìn)制協(xié)議類定義如下:
1 template <class Transport_> 2 3 class TBinaryProtocolT : public TVirtualProtocol< TBinaryProtocolT<Transport_> > { …… }
那么這個(gè)二進(jìn)制協(xié)議類就不用實(shí)現(xiàn)那些虛擬方法了。那些具體的協(xié)議實(shí)現(xiàn)方式后面詳細(xì)介紹。
現(xiàn)在對這部分的類繼承架構(gòu)已經(jīng)分析完畢,后面開始介紹Thrift中實(shí)現(xiàn)的幾個(gè)比較重要的協(xié)議。
浙公網(wǎng)安備 33010602011771號