thrift 的required、optional探究


原因

經常使用thrift來編寫rpc通信,但是對下面兩個問題還是有些疑惑

  1. thrift 的required、optional和不寫有什么區別
  2. optional不設置isset的話被傳輸后值?

實驗

今天就自己編寫代碼測試了一下。如下:

定義book.thrift 如下:

   1: namespace cpp codelab                                                                                                     
   2:  
   3: struct Book {
   4:   1: i32 book_id
   5:   2: string name
   6:   3: optional string optional_attr,
   7:   4: optional string optional_default_val_attr = "optional_default_val_attr",
   8:   5: string default_attr,
   9:   8: string default_val_attr = "default_val_attr",
  10:   10: required string required_attr,
  11:   11: required string required_default_val_attr = "equired_default_val_attr",
  12: }

client代碼如下:

   1: int main(int argc, char **argv) {
   2:   boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
   3:   boost::shared_ptr<TTransport> transport(new TFramedTransport(socket));
   4:   boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
   5:  
   6:   HelloBookClient client(protocol);
   7:   transport->open();
   8:   Book book;
   9:   book.name = "hello thrift";
  10:   printf("book __isset.name: %d\n", book.__isset.name);
  11:   printf("book name: %s\n", book.name.c_str());
  12:   printf("book __isset.optional_attr: %d\n", book.__isset.optional_attr);
  13:   printf("book optional_attr: %s\n", book.optional_attr.c_str());
  14:   printf("book __isset.optional_default_val_attr: %d\n",
  15:         book.__isset.optional_default_val_attr);
  16:   printf("book optional_default_val_attr: %s\n",
  17:         book.optional_default_val_attr.c_str());
  18:   printf("book __isset.default_attr: %d\n", book.__isset.default_attr);
  19:   printf("book default_attr: %s\n", book.default_attr.c_str());
  20:   printf("book __isset.default_val_attr: %d\n",
  21:       book.__isset.default_val_attr);
  22: printf("book default_val_attr: %s\n", book.default_val_attr.c_str());
  23: // printf("book __isset.required_attr: %d\n",
  24: //      book.__isset.required_attr);
  25: printf("book required_attr: %s\n", book.required_attr.c_str());
  26: // printf("book __isset.required_default_val_attr: %d\n",
  27: //     book.__isset.required_default_val_attr);
  28: printf("book required_default_val_attr: %s\n",
  29:       book.required_default_val_attr.c_str());
  30:  
  31: client.ping(book);
  32: transport->close();
  33:  
  34: return 0;
  35:    

Server端代碼:

   1: class HelloBookHandler : virtual public HelloBookIf {                                                                     
   2:  public:
   3:   HelloBookHandler() {
   4:     // Your initialization goes here
   5:   }
   6:  
   7:   void ping(const codelab::Book& book) {
   8:     // Your implementation goes here
   9:     printf("book __isset.name: %d\n", book.__isset.name);
  10:     printf("book name: %s\n", book.name.c_str());
  11:     printf("book __isset.optional_attr: %d\n", book.__isset.optional_attr);
  12:     printf("book optional_attr: %s\n", book.optional_attr.c_str());
  13:     printf("book __isset.optional_default_val_attr: %d\n",
  14:           book.__isset.optional_default_val_attr);
  15:     printf("book optional_default_val_attr: %s\n",
  16:           book.optional_default_val_attr.c_str());
  17:     printf("book __isset.default_attr: %d\n", book.__isset.default_attr);
  18:     printf("book default_attr: %s\n", book.default_attr.c_str());
  19:     printf("book __isset.default_val_attr: %d\n",
  20:           book.__isset.default_val_attr);
  21:     printf("book default_val_attr: %s\n", book.default_val_attr.c_str());
  22:     // printf("book __isset.required_attr: %d\n",
  23:     //      book.__isset.required_attr);
  24:     
  25:     printf("book required_attr: %s\n", book.required_attr.c_str());
  26:     // printf("book __isset.required_default_val_attr: %d\n",
  27:     //     book.__isset.required_default_val_attr);
  28:     printf("book required_default_val_attr: %s\n",
  29:           book.required_default_val_attr.c_str());
  30:   }
  31: };
  32:  
  33: int main(int argc, char **argv) {
  34:   int port = 9090;
  35:   boost::shared_ptr<HelloBookHandler> handler(new HelloBookHandler());
  36:   boost::shared_ptr<TProcessor> processor(new HelloBookProcessor(handler));
  37:   boost::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  38:   boost::shared_ptr<TTransportFactory> transportFactory(
  39:       new TFramedTransportFactory());
  40:   boost::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
  41:  
  42:   TSimpleServer server(processor, serverTransport,
  43:                        transportFactory, protocolFactory);
  44:   server.serve();
  45:   return 0;
  46: }                             

Client端執行結果:

image

Server端執行結果:

image

 

而對client代碼修改如下,

image

server端的執行結果分別如下:

image

image

即沒有設置isset為true的時候optional_attr的值到server被丟失了。而設置為true之后才能在server獲取到

經過上面的測試,得到以下結論:

  1. required字段沒有__isset屬性, 而默認的(就是既沒有required,也沒有optional)和optional的屬性有該方法。
  2. 創建對象的時候,optional和默認的__isset屬性為false,同樣不設置該屬性,而在經過thrift rpc傳輸之后,server端的默認的__isset屬性為true,而optional的__isset的屬性為true。
  3. 有默認值的屬性,不賦值的話,其值就是thrift中的默認值。
  4. optional的如果沒有設置__isset為true,則經過rpc傳輸(或者說是經過序列化再反序列化)之后,其值會丟失。

為什么會有上面的結果呢:

原因分析

   查看thrift生成的文件:

其中book_types.h 如下:

   1: /**
   2:  * Autogenerated by Thrift
   3:  *
   4:  * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
   5:  */
   6: #ifndef book_TYPES_H
   7: #define book_TYPES_H
   8:  
   9: #include <Thrift.h>
  10: #include <TApplicationException.h>
  11: #include <protocol/TProtocol.h>
  12: #include <transport/TTransport.h>
  13:  
  14:  
  15:  
  16: namespace codelab {
  17:  
  18: typedef struct _Book__isset {
  19:   _Book__isset() : book_id(false), name(false), optional_attr(false), optional_default_val_attr(false), default_attr(false), default_val_attr(false) {}
  20:   bool book_id;
  21:   bool name;
  22:   bool optional_attr;
  23:   bool optional_default_val_attr;
  24:   bool default_attr;
  25:   bool default_val_attr;
  26: } _Book__isset;
  27:  
  28: class Book {
  29:  public:
  30:  
  31:   static const char* ascii_fingerprint; // = "EC22AAB82386E1FAA959FB075574467D";
  32:   static const uint8_t binary_fingerprint[16]; // = {0xEC,0x22,0xAA,0xB8,0x23,0x86,0xE1,0xFA,0xA9,0x59,0xFB,0x07,0x55,0x74,0x46,0x7D};
  33:  
  34:   Book() : book_id(0), name(""), optional_attr(""), optional_default_val_attr("optional_default_val_attr"), default_attr(""), default_val_attr("default_val_attr"), required_attr(""), required_default_val_attr("equired_default_val_attr") {
  35:   }
  36:  
  37:   virtual ~Book() throw() {}
  38:  
  39:   int32_t book_id;
  40:   std::string name;
  41:   std::string optional_attr;
  42:   std::string optional_default_val_attr;
  43:   std::string default_attr;
  44:   std::string default_val_attr;
  45:   std::string required_attr;
  46:   std::string required_default_val_attr;
  47:  
  48:   _Book__isset __isset;
  49:  
  50:   bool operator == (const Book & rhs) const
  51:   {
  52:     if (!(book_id == rhs.book_id))
  53:       return false;
  54:     if (!(name == rhs.name))
  55:       return false;
  56:     if (__isset.optional_attr != rhs.__isset.optional_attr)
  57:       return false;
  58:     else if (__isset.optional_attr && !(optional_attr == rhs.optional_attr))
  59:       return false;
  60:     if (__isset.optional_default_val_attr != rhs.__isset.optional_default_val_attr)
  61:       return false;
  62:     else if (__isset.optional_default_val_attr && !(optional_default_val_attr == rhs.optional_default_val_attr))
  63:       return false;
  64:     if (!(default_attr == rhs.default_attr))
  65:       return false;
  66:     if (!(default_val_attr == rhs.default_val_attr))
  67:       return false;
  68:     if (!(required_attr == rhs.required_attr))
  69:       return false;
  70:     if (!(required_default_val_attr == rhs.required_default_val_attr))
  71:       return false;
  72:     return true;
  73:   }
  74:   bool operator != (const Book &rhs) const {
  75:     return !(*this == rhs);
  76:   }
  77:  
  78:   bool operator < (const Book & ) const;
  79:  
  80:   uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
  81:   uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
  82:  
  83: };
  84:  
  85: } // namespace
  86:  
  87: #endif

book_types.cpp 文件如下:

   1: /**
   2:  * Autogenerated by Thrift
   3:  *
   4:  * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
   5:  */
   6: #include "codelab/thrift/proto/gen-cpp/book_types.h"
   7:  
   8: namespace codelab {
   9:  
  10: const char* Book::ascii_fingerprint = "EC22AAB82386E1FAA959FB075574467D";
  11: const uint8_t Book::binary_fingerprint[16] = {0xEC,0x22,0xAA,0xB8,0x23,0x86,0xE1,0xFA,0xA9,0x59,0xFB,0x07,0x55,0x74,0x46,0x7D};
  12:  
  13: uint32_t Book::read(::apache::thrift::protocol::TProtocol* iprot) {
  14:  
  15:   uint32_t xfer = 0;
  16:   std::string fname;
  17:   ::apache::thrift::protocol::TType ftype;
  18:   int16_t fid;
  19:  
  20:   xfer += iprot->readStructBegin(fname);
  21:  
  22:   using ::apache::thrift::protocol::TProtocolException;
  23:  
  24:   bool isset_required_attr = false;
  25:   bool isset_required_default_val_attr = false;
  26:  
  27:   while (true)
  28:   {
  29:     xfer += iprot->readFieldBegin(fname, ftype, fid);
  30:     if (ftype == ::apache::thrift::protocol::T_STOP) {
  31:       break;
  32:     }
  33:     switch (fid)
  34:     {
  35:       case 1:
  36:         if (ftype == ::apache::thrift::protocol::T_I32) {
  37:           xfer += iprot->readI32(this->book_id);
  38:           this->__isset.book_id = true;
  39:         } else {
  40:           xfer += iprot->skip(ftype);
  41:         }
  42:         break;
  43:       case 2:
  44:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  45:           xfer += iprot->readString(this->name);
  46:           this->__isset.name = true;
  47:         } else {
  48:           xfer += iprot->skip(ftype);
  49:         }
  50:         break;
  51:       case 3:
  52:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  53:           xfer += iprot->readString(this->optional_attr);
  54:           this->__isset.optional_attr = true;
  55:         } else {
  56:           xfer += iprot->skip(ftype);
  57:         }
  58:         break;
  59:       case 4:
  60:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  61:           xfer += iprot->readString(this->optional_default_val_attr);
  62:           this->__isset.optional_default_val_attr = true;
  63:         } else {
  64:           xfer += iprot->skip(ftype);
  65:         }
  66:         break;
  67:       case 5:
  68:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  69:           xfer += iprot->readString(this->default_attr);
  70:           this->__isset.default_attr = true;
  71:         } else {
  72:           xfer += iprot->skip(ftype);
  73:         }
  74:         break;
  75:       case 8:
  76:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  77:           xfer += iprot->readString(this->default_val_attr);
  78:           this->__isset.default_val_attr = true;
  79:         } else {
  80:           xfer += iprot->skip(ftype);
  81:         }
  82:         break;
  83:       case 10:
  84:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  85:           xfer += iprot->readString(this->required_attr);
  86:           isset_required_attr = true;
  87:         } else {
  88:           xfer += iprot->skip(ftype);
  89:         }
  90:         break;
  91:       case 11:
  92:         if (ftype == ::apache::thrift::protocol::T_STRING) {
  93:           xfer += iprot->readString(this->required_default_val_attr);
  94:           isset_required_default_val_attr = true;
  95:         } else {
  96:           xfer += iprot->skip(ftype);
  97:         }
  98:         break;
  99:       default:
 100:         xfer += iprot->skip(ftype);
 101:         break;
 102:     }
 103:     xfer += iprot->readFieldEnd();
 104:   }
 105:  
 106:   xfer += iprot->readStructEnd();
 107:  
 108:   if (!isset_required_attr)
 109:     throw TProtocolException(TProtocolException::INVALID_DATA);
 110:   if (!isset_required_default_val_attr)
 111:     throw TProtocolException(TProtocolException::INVALID_DATA);
 112:   return xfer;
 113: }
 114:  
 115: uint32_t Book::write(::apache::thrift::protocol::TProtocol* oprot) const {
 116:   uint32_t xfer = 0;
 117:   xfer += oprot->writeStructBegin("Book");
 118:   xfer += oprot->writeFieldBegin("book_id", ::apache::thrift::protocol::T_I32, 1);
 119:   xfer += oprot->writeI32(this->book_id);
 120:   xfer += oprot->writeFieldEnd();
 121:   xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 2);
 122:   xfer += oprot->writeString(this->name);
 123:   xfer += oprot->writeFieldEnd();
 124:   if (this->__isset.optional_attr) {
 125:     xfer += oprot->writeFieldBegin("optional_attr", ::apache::thrift::protocol::T_STRING, 3);
 126:     xfer += oprot->writeString(this->optional_attr);
 127:     xfer += oprot->writeFieldEnd();
 128:   }
 129:   if (this->__isset.optional_default_val_attr) {
 130:     xfer += oprot->writeFieldBegin("optional_default_val_attr", ::apache::thrift::protocol::T_STRING, 4);
 131:     xfer += oprot->writeString(this->optional_default_val_attr);
 132:     xfer += oprot->writeFieldEnd();
 133:   }
 134:   xfer += oprot->writeFieldBegin("default_attr", ::apache::thrift::protocol::T_STRING, 5);
 135:   xfer += oprot->writeString(this->default_attr);
 136:   xfer += oprot->writeFieldEnd();
 137:   xfer += oprot->writeFieldBegin("default_val_attr", ::apache::thrift::protocol::T_STRING, 8);
 138:   xfer += oprot->writeString(this->default_val_attr);
 139:   xfer += oprot->writeFieldEnd();
 140:   xfer += oprot->writeFieldBegin("required_attr", ::apache::thrift::protocol::T_STRING, 10);
 141:   xfer += oprot->writeString(this->required_attr);
 142:   xfer += oprot->writeFieldEnd();
 143:   xfer += oprot->writeFieldBegin("required_default_val_attr", ::apache::thrift::protocol::T_STRING, 11);
 144:   xfer += oprot->writeString(this->required_default_val_attr);
 145:   xfer += oprot->writeFieldEnd();
 146:   xfer += oprot->writeFieldStop();
 147:   xfer += oprot->writeStructEnd();
 148:   return xfer;
 149: }
 150:  
 151: } // namespace
 
仔細查看代碼,上面的問題都可以得到答案:
針對第一個問題:可以從 book_types.h中看到,有一個Book_isset的結構體的成員,
image
而我們看下該_Book__isset結構體的定義如下:
image
可有看到,該結構體重只包含了非required的屬性。因此只有非required屬性才有__isset的屬性。
針對第二個問題:從_Book_isset的構造函數可以看出,對象在構造的時候,其__isset的屬性都是false。而rpc傳出的時候就涉及到對象的序列化和反序列化。
就設計該對象的read和write,我們看下相應的函數:
write函數將對象轉成字符串。代碼如下(限於屏幕值截取部分分析)
image
從代碼可以看出,整個操作以writeStructBegin開始,以writeStructEnd結束。
其對於非optional的字段,是字節調用writeFieldBegin(fileld_name, FieldType, id); writeType() writeFieldEnd 方法處理的。
注意這里write時涉及了thrift中定義的 序號、類型、和名字。因此thrift序號是不可以隨意變動的。
而對於 optional的字段則略有不同,其首先需要判斷屬性的__isset.是否為true,只有為true時才調用相應的write處理。 因此如果optional
字段不設置其__isset,則序列化的時候會不處理,( 解答了第四個問題)
反序列就是read操作:
read是write的逆過程:
其從: image 開始
image 讀取數據
然后是一個fid的switch邏輯,根據fid來處理對應的屬性
在讀到STOP的時候終止while循環:
image
最后是一個 image
這個read的邏輯是對應的,read的結束如下:
image
 
在read操作中對於非required字段,讀取時將其isset設置為true。 這也就是為什么server端isset的值是設置的。
而對於required字段,注意read 最后
image
其對於required字段會進行check,如果沒有,則拋出異常。
因此required字段還有此效果,即必須有該字段。
第三個問題則從頭文件中對象的構造函數可以看出原因。
 
 
到這里,上面的問題就全部都清楚了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM