(一)数据序列化的意义
当我们需要将采集的日志存入文件或者是传输到下一个系统时,需要将数据对象转化为字节流格式,也就是数据的序列化过程。通常情况下,搭建一套数据仓库系统,会经历如下四个阶段:
1.没有序列化方案:该方案将数据定义为字符串格式,并以文本的形式保存,如果存在多条数据,则采用分隔符来分隔,例如“{|[”等。当增加字段时,通过在文本的最后增加新行的方式来进行,修改及解析比较繁琐,修改需要改动的依赖多。
2.采用编程语言内置的序列化方案,例如Java的Serialization等,这种方式会将日志系统与开发语言绑定起来。
3.格式化数据表达方式,如XML、JSON等,该方式能够约束每个字段的类型,为后续的优化和共享带来了便利。
4.采用序列化框架,例如Thrift、Avro、Protobuf等,通过引入schema的概念,使得序列化和维护变得非常高效,并且有了代码的特征。序列化框架的优点有:提供IDL用以描述数据的schema信息;支持跨语言的读写;数据的编码存储能够节约存储空间;支持schema的演化,也就是可以根据一定的规则修改schema。
目前比较常用的开源组建包括Facebook的Thrift、Google的Protobuf、Apache的Avro等,这些方案大同小异,并不存在绝对的优点,需要根据其特点灵活使用。
(二)Thrift方案
Thrift是Facebook开源的RPC框架,具备了序列化和RPC两个功能,几乎支持所有的编程语言,并提供了一套完整的IDL语言用来定义Schema信息,在实际应用中非常广泛。
以下是一个简单的Thrift例子:
// 命名空间定义
include "shared.thrift"
namespace cpp tutorial
// 定义别名
typedef i32 MyInteger
const i32 INT32CONSTANT = 9853
// 定义枚举类型
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
// 定义结构体
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
5: required int id
}
Thrift通过struct关键字来描述对象,由四种属性构成:
1.域编号:每个域必须是唯一的整数,可以不连续,通过该编号Thrift能够实现向前及向后的兼容性;
2.域修饰:包括required和optional两个关键字,用来对域的值进行限制;
3.域类型:包括int、long等基本类型,也支持set、list、map等复杂容器;
4.域名称:同一个struct下的域名必须唯一。
一旦给出了数据的Thrift IDL定以后,可以通过Thrift提供的编译器直接生成目标语言代码,例如Java:
thrift --gen java tutorial.thrift
下面是Java使用例子:
public class HelloServerDemo {
public static final int SERVER_PORT = 8090;
public void startServer() {
try {
System.out.println("HelloWorld TSimpleServer start ....");
//在这里调用了 HelloWorldIm