zoukankan      html  css  js  c++  java
  • 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
    ", book.__isset.name);
      11:   printf("book name: %s
    ", book.name.c_str());
      12:   printf("book __isset.optional_attr: %d
    ", book.__isset.optional_attr);
      13:   printf("book optional_attr: %s
    ", book.optional_attr.c_str());
      14:   printf("book __isset.optional_default_val_attr: %d
    ",
      15:         book.__isset.optional_default_val_attr);
      16:   printf("book optional_default_val_attr: %s
    ",
      17:         book.optional_default_val_attr.c_str());
      18:   printf("book __isset.default_attr: %d
    ", book.__isset.default_attr);
      19:   printf("book default_attr: %s
    ", book.default_attr.c_str());
      20:   printf("book __isset.default_val_attr: %d
    ",
      21:       book.__isset.default_val_attr);
      22: printf("book default_val_attr: %s
    ", book.default_val_attr.c_str());
      23: // printf("book __isset.required_attr: %d
    ",
      24: //      book.__isset.required_attr);
      25: printf("book required_attr: %s
    ", book.required_attr.c_str());
      26: // printf("book __isset.required_default_val_attr: %d
    ",
      27: //     book.__isset.required_default_val_attr);
      28: printf("book required_default_val_attr: %s
    ",
      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
    ", book.__isset.name);
      10:     printf("book name: %s
    ", book.name.c_str());
      11:     printf("book __isset.optional_attr: %d
    ", book.__isset.optional_attr);
      12:     printf("book optional_attr: %s
    ", book.optional_attr.c_str());
      13:     printf("book __isset.optional_default_val_attr: %d
    ",
      14:           book.__isset.optional_default_val_attr);
      15:     printf("book optional_default_val_attr: %s
    ",
      16:           book.optional_default_val_attr.c_str());
      17:     printf("book __isset.default_attr: %d
    ", book.__isset.default_attr);
      18:     printf("book default_attr: %s
    ", book.default_attr.c_str());
      19:     printf("book __isset.default_val_attr: %d
    ",
      20:           book.__isset.default_val_attr);
      21:     printf("book default_val_attr: %s
    ", book.default_val_attr.c_str());
      22:     // printf("book __isset.required_attr: %d
    ",
      23:     //      book.__isset.required_attr);
      24:     
      25:     printf("book required_attr: %s
    ", book.required_attr.c_str());
      26:     // printf("book __isset.required_default_val_attr: %d
    ",
      27:     //     book.__isset.required_default_val_attr);
      28:     printf("book required_default_val_attr: %s
    ",
      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字段还有此效果,即必须有该字段。
    第三个问题则从头文件中对象的构造函数可以看出原因。
     
     
    到这里,上面的问题就全部都清楚了。
  • 相关阅读:
    git
    HTML5 新增语义化标签
    vue directive 常用指令
    JS 数组 数组迭代方法 map, forEach, filter, some, every,
    图片居中
    进度条
    移动页面 REM自适应
    轮播图基本样式
    webpack3.0
    关于码云中项目提交的问题
  • 原文地址:https://www.cnblogs.com/lovemdx/p/3274792.html
Copyright © 2011-2022 走看看