原文地址:zhwen.org/xlog/?p=671apache
thrift的数据类型定义的时候和protobuf(后面简称pb)同样也有requried和optional标签,意义是相似的,required标示改消息在序列化以前必须设置该字段值,若是不设置则没法序列化,固然也就更没法反序列化,该标签标示的字段是必填字段;而optional字 段则标示该字段可赋值也能够不赋值,固然不赋值的结果是在序列化以后的数据中该字段被标示为无值字段,就是说直接访问获取该值是不行的,必须先判断是否设 置了该字段的值,再去读值;这样做的好处是在协议扩充和变动时能够较为灵活的设计程序,并且在传输上也就减小了没必要要的字段传输。数据结构
pb是必须选择requied或optional之一,如不没有标示,用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;
}; |
能够看出thrift在cpp中把struct转换成了class,生成的代码中都把成员变量进行初始化设置,同时在代码中引入了另一个结构体_StUser__isset 来记录这些字段的标签行为,对于requried的字段isset中没有bool变量标示,可是对于optional和无标签字段都有,而后咱们再来看看它的序列化和反序列化函数—write和read函数。在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变量。从代码中能够看出thrift对requried的字段只是做了默认值处理,让它在数据结构初始化的时候就有值,避免在序列化的时候无值。而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); |