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的信息。因此,我们需求变更时,尽量保存旧的字段不要删除,做到只增不减的方式来兼容旧接口。这里字段序号是唯一标识字段的关键。