原因

经常使用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字段还有此效果,即必须有该字段。
第三个问题则从头文件中对象的构造函数可以看出原因。
 
 
到这里,上面的问题就全部都清楚了。
posted on 2013-08-22 14:24  @且听风吟@  阅读(12871)  评论(0编辑  收藏  举报