原因

经常使用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. sql查询静态数据

    select * from ( ,,,'高中')) AS Education ( EducationId,EducationName )

  2. PHP 接口签名验证

    目录 概览 常用验证 单向散列加密 对称加密 非对称加密 密钥安全管理 接口调试工具 在线接口文档 扩展 小结 概览 工作中,我们时刻都会和接口打交道,有的是调取他人的接口,有的是为他人提供接口,在这 ...

  3. Nginx 常用基础模块

    目录 Nginx 常用基础模块 Nginx日志管理 nginx日志切割 Nginx目录索引 Nginx状态监控 Nginx访问控制 Nginx访问限制 Nginx 请求限制配置实战 Nginx Loc ...

  4. Nginx Web 基础入门

    目录 Nginx Web 基础入门 Nginx快速安装 两种方式部署Nginx 如何升级nginx或者添加功能 使用systemd管理nginx nginx相关配置文件 nginx的配置文件详解 虚拟 ...

  5. H2数据库做单测数据库时踩到的坑

    H2数据库用来做单测数据库,可以自定义初始化数据,不用担心数据库内容更改造成单测跑不过问题,不过H2数据库跟实际使用的Mysql还是有一定区别. 1. H2数据库不支持Mysql的批量更新功能,支持批 ...

  6. 【C/C++】知识点系统复习 (第一周)

    2018/12/18 周二 1. C++内存布局分为几个区域,每个区域有什么特点? 主要可以分为 5 个区域, (1) 栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数 ...

  7. webpack第一节(2)

    安装webpack在文件夹中 安装完成如图所示 牛刀小试 在webpack-test根目录下新建一个hello.js (不新建在node-modules文件夹下面的目的是,该文件夹是webpack的依 ...

  8. python 常用技巧 — 列表(list)

    目录: 1. 嵌套列表对应位置元素相加 (add the corresponding elements of nested list) 2. 多个列表对应位置相加(add the correspond ...

  9. MAS(转)

    1.为什么要使用微服务? 要说为什么要使用微服务,我们要先说下传统的企业架构模式-垂直架构/单块架构模式,简单点说:我们一般将系统分为三层架构,但是这是逻辑上的三层,而非物理上的三层,这就意味着经过编 ...

  10. 【leetcode】984. String Without AAA or BBB

    题目如下: Given two integers A and B, return any string S such that: S has length A + B and contains exa ...