原因

经常使用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端执行结果:

Server端执行结果:

 

而对client代码修改如下,

server端的执行结果分别如下:

即没有设置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的结构体的成员,
而我们看下该_Book__isset结构体的定义如下:
可有看到,该结构体重只包含了非required的属性。因此只有非required属性才有__isset的属性。
针对第二个问题:从_Book_isset的构造函数可以看出,对象在构造的时候,其__isset的属性都是false。而rpc传出的时候就涉及到对象的序列化和反序列化。
就设计该对象的read和write,我们看下相应的函数:
write函数将对象转成字符串。代码如下(限于屏幕值截取部分分析)
从代码可以看出,整个操作以writeStructBegin开始,以writeStructEnd结束。
其对于非optional的字段,是字节调用writeFieldBegin(fileld_name, FieldType, id); writeType() writeFieldEnd 方法处理的。
注意这里write时涉及了thrift中定义的序号、类型、和名字。因此thrift序号是不可以随意变动的。
而对于optional的字段则略有不同,其首先需要判断属性的__isset.是否为true,只有为true时才调用相应的write处理。 因此如果optional
字段不设置其__isset,则序列化的时候会不处理,(解答了第四个问题)
反序列就是read操作:
read是write的逆过程:
其从: 开始
读取数据
然后是一个fid的switch逻辑,根据fid来处理对应的属性
在读到STOP的时候终止while循环:
最后是一个
这个read的逻辑是对应的,read的结束如下:
 
在read操作中对于非required字段,读取时将其isset设置为true。这也就是为什么server端isset的值是设置的。
而对于required字段,注意read 最后
其对于required字段会进行check,如果没有,则抛出异常。
因此required字段还有此效果,即必须有该字段。
第三个问题则从头文件中对象的构造函数可以看出原因。
 
 
到这里,上面的问题就全部都清楚了。

thrift 的required、optional探究的更多相关文章

  1. Thrift中required和optional

    最近在搞Thrift,对其字段声明中的required关键字有所误解,仔细调试了一下才明白其真实含义. required的意思不是说声明对象时,必须填这个值,而是Thrift在传输(序列化)过程中无论 ...

  2. How to fix the bug “Expected "required", "optional", or "repeated".”?

    参考:https://github.com/tensorflow/models/issues/1834 You need to download protoc version 3.3 (already ...

  3. Thrift.0

    0. Thrift的特性 1. 安装Thrift编译器 [Todo] http://thrift.apache.org/docs/install/ http://thrift.apache.org/d ...

  4. Netty学习——Thrift的入门使用

    Netty学习——Thrift的入门使用 希望你能够,了解并使用它.因为它是一个效率很高的框架 官网地址:http://thrift.apache.org/ 1.Thrift数据类型 一门技术如果需要 ...

  5. Google 开源技术protobuf

    http://blog.csdn.net/hguisu/article/details/20721109#0-tsina-1-1601-397232819ff9a47a7b7e80a40613cfe1 ...

  6. Potocol Buffer详解

    protocol安装及使用 上一篇博文介绍了一个综合案例,这篇将详细介绍protocol buffer. 为什么使用protocol buffer? java默认序列化效率较低. apache的thr ...

  7. gRPC学习

    概述 gRPC 一开始由 google 开发,是一款语言中立.平台中立.开源的远程过程调用(RPC)系统. 在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法, ...

  8. Storm Flow

    A Stream represents the core data model in Trident, and can be thought of as a "stream" of ...

  9. iOS:以前笔记,未整理版。太多了,先放着吧。。。。。。。

    1. -(void)timetick { _d = 0; NSTimer *newtime =[NSTimer scheduledTimerWithTimeInterval:1 target:self ...

随机推荐

  1. Codeforces 358D DP

    题意:有n只兔子需要喂养,每只兔子被喂养后的幸福感取决于与它相邻的兔子中已经被喂养的数量.现在问喂养这n只兔子可以获得的总幸福感最多是多少? 思路:初步分析题目发现,一只兔子被喂养获得的幸福感取决于其 ...

  2. 六、SpringBoot配置@ConfigurationProperties与@Value区别

    1.@Value的使用 三种用法 // ${key} 从环境变量.配置文件中取值 @Value("${person.last-name}") private String last ...

  3. 【串线篇】浅谈BeanFactory

    BeanFactory&ApplicationContext BeanFactory: bean工厂接口,负责创建bean实例, 容器里保存的所有单例bean其实是一个map<key-- ...

  4. C++ 字符串截取转换及字符流控制

    文章由来 ------------------工作需要缓冲区里的字符串控制,还是混合编译的那种,根据协议来定义截取各种字符流,控制大小长度,截取返回的内容然后转换成特定的类型, 可能表述不是那么正确, ...

  5. OpenCV常用基本处理函数(3)颜色空间

    颜色空间转换 对图像进行颜色空间转换,比如从 BGR 到灰度图,或者从BGR 到 HSV 等 我们要用到的函数是:cv2.cvtColor(input_image ,flag),其中 flag就是转换 ...

  6. Python技能树

    本博客Python内容的索引,以后就照着它写了.

  7. permutations and combinations

    # import itertools # # my_list = [1, 2, 3, 4, 5, 6] # # combinations = itertools.combinations(my_lis ...

  8. SCP-bzoj-1079

    项目编号:bzoj-1079 项目等级:Safe 项目描述: 戳这里 特殊收容措施: DP.普通的状压状态数515,显然TLE+MLE,我们考虑把底数和幂换一换,压成155的状态数. 故状态设计为:f ...

  9. Database基础(七):部署集群基础环境、MySQL-MMM架构部署、MySQL-MMM架构使用

    一.部署集群基础环境 目标: 本案例要求为MySQL集群准备基础环境,完成以下任务操作: 数据库授权 部署MySQL双主多从结构 配置本机hosts解析记录 方案: 使用4台RHEL 6虚拟机,如下图 ...

  10. Python基础(二):斐波那契数列、模拟cp操作、生成8位随机密码

    一.斐波那契数列 目标: 编写fib.py脚本,主要要求如下: 输出具有10个数字的斐波那契数列 使用for循环和range函数完成 改进程序,要求用户输入一个数字,可以生成用户需要长度的斐波那契数列 ...