thrift 一个有意思的特性:Class名称无关性

简介: 最近开发的一个项目,后端采用thrift框架来提供rpc服务(java语言实现),然后前端采用php语言来生成thrift client调用后台RPC服务。由于某些原因,上周我把thrift定义文件中一个struct名称修改了,当然也没多想,顺手就把java服务端重新编译部署,而php前端的部署未做任何变化,按常规理解,服务契约中的类名,从A改成B,服务的调用方理应同步更新部署,否则感觉应该会出错。

最近开发的一个项目,后端采用thrift框架来提供rpc服务(java语言实现),然后前端采用php语言来生成thrift client调用后台RPC服务。由于某些原因,上周我把thrift定义文件中一个struct名称修改了,当然也没多想,顺手就把java服务端重新编译部署,而php前端的部署未做任何变化,按常规理解,服务契约中的类名,从A改成B,服务的调用方理应同步更新部署,否则感觉应该会出错。

然而,美好的事情就这么发生了,一切运行正常,依旧丝丝顺滑!

再然后,我就开始思考人生,重新理解 thrift内部的序列化与反序列化机制,很快就想明白了,借用之前写过的博客rpc框架之 avro 学习 2 - 高效的序列化中的一张图:

 thrift内部存储二进制数据时,为了提高存储效率,每个field都分配了一个数字编号,所以在序列化及反序列化时,其实是只认数字编号,不管名称的,这也正是thrift IDL文件定义struct时,为什么强制要求每个成员都要指定一个在struct本身范围内不重复的数字序号

struct PersonModel {
  1: i16 age = 0,
  2: string name,
  3: bool sex,
  4: double salary,
  5: byte childrenCount
}

IDL生成的具体语言的源代码中,解析对象时,同样也只看序号,以c#生成的代码为例:

 1    public void Read (TProtocol iprot)
 2     {
 3       iprot.IncrementRecursionDepth();
 4       try
 5       {
 6         TField field;
 7         iprot.ReadStructBegin();
 8         while (true)
 9         {
10           field = iprot.ReadFieldBegin();
11           if (field.Type == TType.Stop) { 
12             break;
13           }
14           switch (field.ID)
15           {
16             case 1:
17               if (field.Type == TType.I16) {
18                 Age = iprot.ReadI16();
19               } else { 
20                 TProtocolUtil.Skip(iprot, field.Type);
21               }
22               break;
23             case 2:
24               if (field.Type == TType.String) {
25                 Name = iprot.ReadString();
26               } else { 
27                 TProtocolUtil.Skip(iprot, field.Type);
28               }
29               break;
30             case 3:
31               if (field.Type == TType.Bool) {
32                 Sex = iprot.ReadBool();
33               } else { 
34                 TProtocolUtil.Skip(iprot, field.Type);
35               }
36               break;
37             case 4:
38               if (field.Type == TType.Double) {
39                 Salary = iprot.ReadDouble();
40               } else { 
41                 TProtocolUtil.Skip(iprot, field.Type);
42               }
43               break;
44             case 5:
45               if (field.Type == TType.Byte) {
46                 ChildrenCount = iprot.ReadByte();
47               } else { 
48                 TProtocolUtil.Skip(iprot, field.Type);
49               }
50               break;
51             default: 
52               TProtocolUtil.Skip(iprot, field.Type);
53               break;
54           }
55           iprot.ReadFieldEnd();
56         }
57         iprot.ReadStructEnd();
58       }
59       finally
60       {
61         iprot.DecrementRecursionDepth();
62       }
63     }

从上面的case语句可以很清楚的看出,代码内部只认数字序号,不关心名称。

 

结论:只要不改变struct内部的成员类型和数字编号,struct对应的类名可以放心大胆的修改。 

目录
相关文章
|
2月前
|
JSON Java 数据格式
使用`MockMvc`额外的补充和高级用法
使用`MockMvc`额外的补充和高级用法
33 3
|
3月前
|
编译器 C#
C#.Net筑基-类型系统②常见类型 --record是什么类型?
`record`在C#中是一种创建简单、只读数据结构的方式,常用于轻量级数据传输。它本质上是类(默认)或结构体的快捷形式,包含自动生成的属性、`Equals`、`ToString`、解构赋值等方法。记录类型可以继承其他record或接口,但不继承普通类。支持使用`with`语句创建副本。例如,`public record User(string Name, int Age)`会被编译为包含属性、相等比较和`ToString()`等方法的类。记录类型提供了解构赋值和自定义实现,如密封的`sealed`记录,防止子类重写。
|
3月前
|
缓存 监控 Java
Hysterix的概念、作用、使用方法
Hysterix的概念、作用、使用方法
35 0
|
3月前
|
存储 Oracle Java
Java 包和 API 深度解析:组织代码,避免命名冲突
Java 中的包 用于将相关的类分组在一起。可以将其视为文件目录中的一个文件夹。我们使用包来避免名称冲突,并编写更易于维护的代码。 包分为两类: 内置包(来自 Java API 的包) 用户定义的包(创建自己的包)
330 2
|
Java
【java】【作业】定义课程信息;继承和组合练习
【java】【作业】定义课程信息;继承和组合练习
102 0
Java——三大特性之一:封装(概念理解+应用举例)
Java——三大特性之一:封装(概念理解+应用举例)
Java——三大特性之一:封装(概念理解+应用举例)
|
数据采集 负载均衡 搜索推荐
会计学包含的两种程序设计思想
会计学包含的两种程序设计思想
会计学包含的两种程序设计思想
|
存储 Java
Java的String类中提到的代码点,代码单元到底是什么?
Java的String类中提到的代码点,代码单元到底是什么?
111 0
Java的String类中提到的代码点,代码单元到底是什么?
教你借助占位符定制资源内容 | 带你学《Java语言高级特性》之三十
一成不变的内容往往只能满足基本要求,多姿多彩的时间才是众望所归。本节将带领读者通过占位符和MessageFormat类的配合实现对资源内容的动态化定制工作。
教你借助占位符定制资源内容 | 带你学《Java语言高级特性》之三十
|
程序员
Attribute(特性),怎么用才更好? —— 字段编号被误解了
  上一篇里(Attribute(特性),怎么用才更好? ),有人说,“坚决杜绝magic number ”,这个magic number指的就是字段编号吧,其实您误解了。   一提到字段编号,可能有些人的第一反应就是这样的用法:     Person1.2000020,或者Person1[2000020],或者ds[2000020]。
880 0