原因

经常使用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. 十五、API请求接口-远程服务器返回错误: (400) 错误的请求错误

    一.远程服务器返回错误: (400) 错误的请求错误 捕获异常查看具体错误 using Newtonsoft.Json; using System; using System.Collections. ...

  2. STM点滴一

    就就是你用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险.也就不需要关闭中断. This way, there is no risk that an IRQ occurs between ...

  3. JavaSE---IO体系

    1.BIO 1.1 Block IO,同步阻塞IO: 1.2 eg:java.io   包下的      InputStream . OutputStream.  Writer.Reader... j ...

  4. CSS 布局 - Overflow

    CSS 布局 - Overflow CSS overflow 属性用于控制内容溢出元素框时显示的方式. 这里的文本内容是可以滚动的,滚动条方向是垂直方向.dd马达价格 这里的文本内容是可以滚动的,滚动 ...

  5. PHP filter_has_var() 函数

    「大理石平台」大理石平台上的裂缝是怎么回事? 定义和用法 filter_has_var() 函数检查是否存在指定输入类型的变量. 如果成功则返回 TRUE,如果失败则返回 FALSE. 语法 filt ...

  6. 初学Linux基本的命令操作应当记牢

    Linux管理文件和目录的命令 命令 功能 命令 功能 pwd 显示当前目录 ls 查看目录下的内容 cd 改变所在目录 cat 显示文件的内容 grep 在文件中查找某字符 cp 复制文件 touc ...

  7. 从输入 URL 到页面展示,到底发生了什么

    从输入 URL 到页面展示,到底发生了什么 1.输入URL 当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应 ...

  8. undefined null测试

    测试浏览器:chrome 当有父元素的子元素未定义时undefined和null均为true,类型为undefined 当元素赋给null后undefined和null均为true,类型为object ...

  9. js全局变量优点和缺点

    全局变量的优点:可以减少变量的个数,减少由于实际参数和形式参数的数据传递带来的时间消耗. 全局变量的缺点: (1)全局变量保存在静态存贮区,程序开始运行时为其分配内存,程序结束释放该内存.与局部变量的 ...

  10. 接口调用post请求参数在body中

    package com.ynhrm.common.utils; import com.alibaba.fastjson.JSONObject; import lombok.Data; import o ...