Thrift的required和optional源碼分析

原文地址:zhwen.org/xlog/?p=671apache


thrift的數據類型定義的時候和protobuf(後面簡稱pb)同樣也有requriedoptional標籤,意義是相似的,required標示改消息在序列化以前必須設置該字段值,若是不設置則沒法序列化,固然也就更沒法反序列化,該標籤標示的字段是必填字段;而optional字 段則標示該字段可賦值也能夠不賦值,固然不賦值的結果是在序列化以後的數據中該字段被標示爲無值字段,就是說直接訪問獲取該值是不行的,必須先判斷是否設 置了該字段的值,再去讀值;這樣做的好處是在協議擴充和變動時能夠較爲靈活的設計程序,並且在傳輸上也就減小了沒必要要的字段傳輸。數據結構

pb是必須選擇requiedoptional之一,如不沒有標示,用proto編譯是會報錯的:函數

protoc –cpp_out ./ test.proto

test.proto:4:5: Expected 「required」, 「optional」, or 「repeated」.測試

1 message Person {ui

2 required string name = 1;this

3 required int32 id = 2;spa

4 string email = 3;.net

5 }設計

 

    可是thrift還有無標籤類型數據,也是由於thrift支持了跟多的協議,而pb能夠說只是支持一種數據傳輸協議。thrift的無標籤字段若是沒有賦值那麼就是空的,在傳輸過程當中也會繼續傳輸該值,今天來分析一下。首先寫一個測試的thrift文件來編譯:blog

namespace cpp xlight

struct StUser {

1: required i32 userId;

2: optional string userName;

4: string language;

}

/// 編譯

helight:rpc$thrift –gen cpp -out ./ test.thrift

    上面的測試文件能夠看出,有些字段設置了requied,有些字段設置了optional,有些字段是什麼也沒有設置,就只是字段類型和字段名。先看看編譯後的文件:test_types.h

typedef struct _StUser__isset {

_StUser__isset() : userName(false), language(false) {}

bool userName;

bool language;

} _StUser__isset;

 

class StUser {

public:

 

static const char* ascii_fingerprint; // = 「76285C3D933C871361DFACF1222DDAAE」;

static const uint8_t binary_fingerprint[16]; // = {0×76,0×28,0x5C,0x3D,0×93,0x3C,0×87,0×13,0×61,0xDF,0xAC,0xF1,0×22,0x2D,0xDA,0xAE};

 

StUser() : userId(0), userName(), language() {

}

 

virtual ~StUser() throw() {}

 

int32_t userId;

std::string userName;

std::string language;

_StUser__isset __isset;

 

void __set_userId(const int32_t val) {

userId = val;

}

 

void __set_userName(const std::string& val) {

userName = val;

__isset.userName = true;

}

 

void __set_language(const std::string& val) {

language = val;

}

bool operator == (const StUser & rhs) const

{

if (!(userId == rhs.userId))

return false;

if (__isset.userName != rhs.__isset.userName)

return false;

else if (__isset.userName && !(userName == rhs.userName))

return false;

if (!(language == rhs.language))

return false;

return true;

}

bool operator != (const StUser &rhs) const {

return !(*this == rhs);

}

 

bool operator < (const StUser & ) const;

 

uint32_t read(::apache::thrift::protocol::TProtocol* iprot);

uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;

 

};

 

    能夠看出thriftcpp中把struct轉換成了class,生成的代碼中都把成員變量進行初始化設置,同時在代碼中引入了另一個結構體_StUser__isset 來記錄這些字段的標籤行爲,對於requried的字段isset中沒有bool變量標示,可是對於optional和無標籤字段都有,而後咱們再來看看它的序列化和反序列化函數—writeread函數。在test_types.cpp

    代碼過多就不粘貼了:主要是在write的時候會判斷optional是否被設置,若是沒有被設置則不進行序列化。其它兩種字段都會序列化。

if (this->__isset.userName) {

xfer += oprot->writeFieldBegin(「userName」, ::apache::thrift::protocol::T_STRING, 2);

xfer += oprot->writeString(this->userName);

xfer += oprot->writeFieldEnd();

}

    在反序列化函數中:對requried字段作了標示,若是這個字段沒有則會拋出異常,其它兩個字段在反序列化以後都會設置其isset變量。從代碼中能夠看出thriftrequried的字段只是做了默認值處理,讓它在數據結構初始化的時候就有值,避免在序列化的時候無值。而pb對這塊的處理是若是不設置requried字段,程序運行會失敗。

case 1:

if (ftype == ::apache::thrift::protocol::T_I32) {

xfer += iprot->readI32(this->userId);

isset_userId = true;

} else {

xfer += iprot->skip(ftype);

}

break;

case 2:

if (ftype == ::apache::thrift::protocol::T_STRING) {

xfer += iprot->readString(this->userName);

this->__isset.userName = true;

} else {

xfer += iprot->skip(ftype);

}

break;

case 4:

if (ftype == ::apache::thrift::protocol::T_STRING) {

xfer += iprot->readString(this->language);

this->__isset.language = true;

} else {

xfer += iprot->skip(ftype);

}

break;

default:

xfer += iprot->skip(ftype);

break;

。。。

if (!isset_userId)

throw TProtocolException(TProtocolException::INVALID_DATA);

相關文章
相關標籤/搜索