我们对apache thrift的使用:thrift最初由facebook开发,用来构建跨语言服务。它可以用于诸多目的,但我们将缩小讨论范围,只关注它作为一个序列化框架的使用。
其他序列化框架
还有其他类似于apache thrift的工具,如protocol buffers和avro。记住,本书不是对每种情况的所有可能的工具提供调研,而是选用适当的工具来说明基本概念。作为一种序列化框架,thrift已经经过了生产环境的完全测试并得到了广泛使用。
thrift的核心是结构体(struct)和联合体(union)的类型定义。它们是由其他字段组成的,如:
原始数据类型(string,integer,long与double)
其他类型的集合(list,map与set)
其他结构体和联合体
一般来说,用联合体来表示节点是很有用的,结构体是边的自然表示,属性则将联合体和结构体结合起来使用。这对于需要表示superwebanalytics.com模式组件的类型定义来说是有效的。
对于superwebanalytics.com用户节点,通过用户id或浏览器cookie标识某个人,而不会同时用这两个元素标识他。这种模式对于节点是常见的,且完全匹配联合体数据类型—单个值可能有几种表示方式。
在thrift中,联合体通过罗列出所有可能的表示来定义。下面的代码使用thrift联合体定义superwebanalytics.com节点:

注意:联合体还可用于单一表现形式的节点。联合体允许模式随着数据的演变而演变—我们稍后将在本节中进一步讨论这个问题。
每条边都可以表示为包含两个节点的结构体。边结构体的名称表明了它所代表的关系,边结构体中的字段包含了参与该关系的实体。
该模式定义非常简单:
thrift结构体的字段可以表示为required或optional。如果一个字段被定义为required,那么必须为该字段提供一个值,否则thrift会在序列化或反序列化时给出一个错误。因为图模式中的每条边都必须有两个节点,所以在这个例子中它们是必需的字段。
最后让我们来定义属性。属性包含一个节点和属性的值。因为值可以是诸多类型中的一种,所以最好使用联合体结构来表示。
首先定义页面属性的模式。页面只有一个属性,所以很简单:
接下来定义人的属性。正如你看到的,位置属性更为复杂,需要另一个结构体来定义:
位置结构体是很有意思的,因为city、state和country字段可能被存储为单独的数据片。在这种情况下,它们是密切相关的,因此把它们都放在一个结构体作为可选字段是讲得通的。当使用位置信息时,你可能会希望得到所有字段。
此时,边和属性被定义为不同的类型。在理想情况下,你想将所有数据存储在一起,为访问你的信息只提供一个接口。此外,如果存储在单个数据集中,还能使得数据更容易管理。这可以通过将每个属性和边的类型封装到dataunit联合体来实现—如代码清单3-1。
代码清单3-1 完成superwebanalytics.com模式
每个dataunit与其元数据成对保存在一个pedigree结构体中。pedigree包含了信息的时间戳,但也可能包含调试信息或数据源。最后的data结构体对应了基于行为模型中的一个行为。
thrift的设计使得模式可以随时间而演变。这是一个至关重要的属性,因为随着业务需求的改变,你将需要添加新类型的数据,并且想尽可能轻松地这样做。
演变thrift模式的关键是与每个字段相关联的数值标识符。把这些序列化形式的id用于标识字段。当想要改变模式但仍要向下兼容现有的数据时,你必须遵守以下规则:
字段可能被重新命名。这是因为对象的序列化形式使用字段id而不是名称来标识字段。
一个字段可能被删除,但你绝不能重用那个字段id。当反序列化现有数据时,thrift将忽略字段id没有包含在模式中的所有字段。如果你重用之前删除的字段id,thrift会尝试反序列化旧数据到新的字段,这将导致无效或不正确的数据。
只有可选的字段可以被添加到现有的结构体。你不能添加必需的字段,因为现有的数据不会有这些字段,所以不能被反序列化。(注意:这并不适用于联合体,因为联合体没有必需和可选字段的概念)
作为一个例子,如果想要改变superwebanalytics.com模式来存储一个人的年龄和网页之间的链接,那么应对thrift定义文件做出以下改变(变化的用粗体表示)。
代码清单3-2 扩展superwebanalytics.com
注意:添加新的年龄属性是通过添加到相应的联合体结构完成的,并且新的边可以通过将它添加到dataunit联合体来实现内嵌。