zoukankan      html  css  js  c++  java
  • Thrift之TProtocol类体系原理及源码详细解析之其他协议类和总结

    我的新浪微博:http://weibo.com/freshairbrucewoo

    欢迎大家相互交流,共同提高技术。

    第六节 其他协议类

    主要的协议类基本上已经介绍完毕了,当然如果你有更好的实现和思路也可以实现自己的协议类,只要按照我前面介绍的类层次结构继承就可以了。除了前面几节介绍的协议类,Thrift还实现了一些自己内部使用的协议类,例如TDebugProtocol类,采用开发人员可读的文本协议,有助于调试,又例如TProtocolTap类,它可以使用两种协议类进行两次协议转换。放一个窃听装置在协议对象,任何读取这个类都是通过一个封闭的协议对象的,但也反映为写第二个协议对象,还有一个就是用于异常的协议类了。

    到此为止,Thrift实现的协议类基本上介绍完毕了,从这些协议类的特征来看:

    (1)都是实现了对外提供的统一接口,所以每一个协议类可以随时的单独使用,可以很方便的用一个协议类替换另一个协议类,对于实现都是完全独立的,协议类直接没有任何关系(继承除外);

    (2)为了扩展更多的协议类提供了良好的设计方式。

    第七节 总结

    (1)关于定义idl的一些总结,尽量避免定义过于复杂的数据结构。

    从上面的协议分析来看,复合数据类型的存在着一个递归包含的关系。不过thrift在生成封/解包的代码里,并没有出现递归调用来封/解包,而是采用了循环嵌套循环的方式来生成代码,这种做法避免了频繁递归调用封/解包函数,可提高封/解包的效率。同时带来的问题就是生成的代码量的急剧膨胀。

    虽然没有递归调用来封/解包, 如果定义太过于复杂的数据结构也会随之产生多重循环,看下面的例子。假设定义以下一个数据结构:

    map< string, list< set<string> > >,thrift将会产生类似于以下的循环来进行封/解包:

    foreach (key in map)

    {

    foreach ( set in map[key] )

    {

    foreach (string in set )

    encode()/decode();   

    }

    }

    假设map,list,set的元素各有100个,这将是一个严重影响性能的地方,应该避免。

    (2)建议使用了unsigned long long的字段使用string类型,而不是u64类型,因为目前的thrift不支持(不过好像最新版本是支持了的)。

    (3)在网络IO层一个潜在的瓶颈

    由于thrift的binaryprotocol协议的包头没有任何的字节描述了整个网络包的长度的信息。所以thrift的binaryprotocol协议在解包的时候是每次都只能采用从socket读取一个变量的类型接着读取变量的值出来这样的解释方式。

    这种解包的方式可能引起的潜在问题:当请求的client数量非常多,交互的数据量也非常多(这里可能是交互了很多字段,或者使用了太多复合数据类型)时,tcp/ip协议栈的缓冲区可能会被塞满了还没有被处理的数据,就会严重影响服务质量。至于为何不提供某些字节来标识整个数据包的长度,是因为thrift的binaryprotocol协议需要支持复杂数据类型,像set,list,map,而这些复合数据类型的大小是难以确定的。为了支持标识整个数据包长度,封包前需要知道set,list,map的总体大小,那么就需要遍历set,list,map的大小,这是相当不划算,会增加运算逻辑,而且还会导致协议变得很复杂。

    (4)如何做到兼容旧接口

    当我们的server更新接口的同时,还需要保证旧client能够和新server交互,那么在定义IDL时就需要特别注意。假设,我们定义以下一个这个一个结构体来交互用户信息。

    struct user

    {

    1:username string,

    2:password string,

    3:sex i16, 

    }

    当我们需求变更时,假设以下两种情况:

    a)需要新增字段, age来表示年龄

    struct user

    {

    1:username string,

    2:password string,

    3:sex i16, 

    4:age i32,

    }

    注意,原来字段的序号标识一定不能被改变,1:username string, 不能改成 5:username string。此时,如果server是新的,client是旧的,并不影响client的工作,client从server那边收到的包里包含了age的信息,只是没有decode出来而已。

    b)删除字段sex,新增字段age

    struct user

    {

    1:username string,

    2:password string,

    //3:sex i16, 

    4:age i32, (注意,为了保证a)所定义的client能够和b)的server交互,这里的字段序号必须定义为4)

    }

    此时,如果server是新的,client是a)所生成的也并不影响和b)的server交互,因为client从server那边收到的包虽然没有包含sex的信息,但是client并不会崩溃,只是缺少了sex的信息。因此,我们需求变更时,尽量保存旧的字段不要删除,做到只增不减的方式来兼容旧接口。这里字段序号是唯一标识字段的关键。

  • 相关阅读:
    大数据基础——MR编程应用——对中间件的操作
    Hadoop_Hive整理——原理及配置
    Hadoop&Hive——小结
    mysql_小结之事务
    Linux_大数据与数据仓库
    移动布局小结
    JDBC——小结
    Mysql优化反刍
    NoSql-Verson1.0
    python-6
  • 原文地址:https://www.cnblogs.com/brucewoo/p/2585498.html
Copyright © 2011-2022 走看看