《深入解析YARN架构设计与实现原理》第3章 YARN基础库【3.2】
3.2 第三方开源库3.2.1 Protocol Buffers
Protocol Buffers 是一种轻便高效的结构化数据存储格式, 可以用于结构化数据序列化/反序列化。 它很适合做数据存储或
RPC 的数据交换格式, 常用作通信协议、 数据存储等领域的与语言无关、 平台无关、 可扩展的序列化结构数据格式。 目前支持
C++、 Java、 Python三种语言。 在Google内部, 几乎所有的RPC协议和文件格式都是采用Protocol Buffers。
相比于常见的XML格式, Protocol Buffers官方网站这样描述它的优点:
❑平台无关、 语言无关;
❑高性能, 解析速度是XML的20~100倍;
❑体积小, 文件大小仅是XML的1/10~1/3;
❑使用简单;
❑兼容性好。
通常编写一个Protocol Buffers应用需要以下三步:
1) 定义消息格式文件, 通常以proto作为扩展名;
2) 使用Google提供的Protocol Buffers编译器生成特定语言( 目前支持C++、 Java、 Python三类语言) 的代码文件;
3) 使用Protocol Buffers库提供的API来编写应用程序。
为了说明Protocol Buffers的使用方法, 下面给出一个简单的实例。
该实例中首先定义一个消息格式文件person.proto, 描述了通讯录中一个人的基本信息, 接着用Protocol Buffers提供的方法将
一个人的信息写入文件。
步骤1 定义消息格式文件person.proto, 该文件描述了通讯录中某个人的基本信息, 内容如下:
package tutorial; //自定义的命名空间
option java_package = "com.example.tutorial"; //生成文件的包名
option java_outer_classname = "PersonProtos"; //类名
message Person { //待描述的结构化数据
required string name = 1; //required表示这个字段不能为空
required int32 id = 2; //数字“2”表示字段的数字别名
optional string email = 3; //optional表示该字段可以为空
message PhoneNumber { //内部message
required string number = 1;
optional int32 type = 2;
}r
epeated PhoneNumber phone = 4;
}
步骤2 使用Google提供的Protocol Buffers编译器生成Java语言, 命令如下:
protoc -java_out=. person.proto
注意, 上面的命令运行时的当前路径是person.proto所在目录。
步骤3 使用Protocol Buffers库提供的API编写应用程序。 该例子创建了一个Person对象, 先将该对象保存到文件example.txt
中, 之后又从文件中读出并打印出来。
public class ProtocolBufferExample {
static public void main(String[] argv) {
Person person1 = Person.newBuilder()
.setName("Dong Xicheng")
.setEmail("dongxicheng@yahoo.com")
.setId(11111)
.addPhone(Person.PhoneNumber.newBuilder()
.setNumber("15110241024")
.setType(0))
.addPhone(Person.PhoneNumber.newBuilder()
.setNumber("01025689654")
.setType(1)).build();
try {
FileOutputStream output = new FileOutputStream("example.txt");
person1.writeTo(output);
output.close();
} catch(Exception e) {
System.out.println("Write Error! ");
} t
ry {
FileInputStream input = new FileInputStream("example.txt");
Person person2 = Person.parseFrom(input);
System.out.println("person2:" + person2);
} catch(Exception e) {
System.out.println("Read Error!");
}
}
}
在YARN中, 所有RPC函数的参数均采用Protocol Buffers定义的, 相比MRv1中基于Writable序列化的方法, Protocol Buffers的引
入使得YARN在向后兼容性和性能方面向前迈进了一大步。
除序列化/反序列化之外, Protocol Buffers也提供了RPC函数的定义方法, 但并未给出具体实现, 这需 要用户自行实现 , 而
YARN则采用了MRv1中Hadoop RPC库, 举例如下:
service ContainerManagerService { //这是YARN自带的ContainerManager协议的定义
rpc startContainer(StartContainerRequestProto) returns (StartContainerResponseProto);
rpc stopContainer(StopContainerRequestProto) returns (StopContainerResponseProto);
rpc getContainerStatus(GetContainerStatusRequestProto) returns (GetContainerStatusResponseProto);
}
在第2章中, 介绍了YARN中的所有RPC协议, 而这些协议全是使用Protocol Buffers定义的, 具体如下:
❑applicationmaster_protocol.proto: 定义了AM与RM之间的协议—Application-MasterProtocol。
❑applicationclient_protocol.proto: 定义了JobClient( 作业提交客户端) 与RM之间的协议—ApplicationClientProtocol。
❑containermanagement_protocol.proto: 定义了AM与NM之间的协议—Container-ManagementProtocol。
❑resourcemanager_administration_protocol.proto: 定义了Admin( 管理员) 与RM之间的通信协议—
ResourceManagerAdministrationProtocol。
❑yarn_protos.proto: 定义了各个协议RPC的参数。
❑ResourceTracker.proto: 定义了NM与RM之间的协议—ResourceTracker。
除了以上几个内核中的协议, YARN还使用Protocol Buffers对MapReduce中的协议进行了重新定义:
❑MRClientProtocol.proto: 定义了JobClient( 作业提交客户端) 与MRAppMaster之间的协议—MRClientProtocol。
❑mr_protos.proto: 定义了MRClientProtocol协议的各个参数。
3.2.2 Apache Avro
Apache Avro 是Hadoop下的一个子项目。 它本身既是一个序列化框架, 同时也实现了RPC的功能。
Avro官网描述Avro的特性和功能如下:
❑丰富的数据结构类型;
❑快速可压缩的二进制数据形式;
❑存储持久数据的文件容器;
❑提供远程过程调用RPC;
❑简单的动态语言结合功能。
相比于Apache Thrift 和Google的Protocol Buffers, Apache Avro具有以下特点:
❑支持动态模式 。 Avro不需要生成代码, 这有利于搭建通用的数据处理系统, 同时避免了代码入侵。
❑数据无须加标签 。 读取数据前, Avro能够获取模式定义, 这使得Avro在数据编码时只需要保留更少的类型信息, 有利于
减少序列化后的数据大小。
❑无须手工分配的域标识 。 Thrift和Protocol Buffers使用一个用户添加的整型域唯一性定义一个字段, 而Avro则直接使用域
名, 该方法更加直观、 更加易扩展。
编写一个Avro应用也需如下三步:
1) 定义消息格式文件, 通常以avro作为扩展名;
2) 使用Avro编译器生成特定语言的代码文件( 可选) ;
3) 使用Avro库提供的API来编写应用程序。
下面给出一个使用实例。
步骤1 定义消息格式文件person.avro, 该文件描述了通讯录中某个人的基本信息, 内容如下:
{"namespace": "com.example.tutorial",
"type": "record",
"name": "Person",
"fields": [
{"name": "name", "type": "string"},
{"name": "id", "type": "int"},
{"name": "email", "type": ["string", "null"]},
{"name": "phone", "type": {"type": "array",
"items": {"type": "record", "name": "PhoneNumber",
"fields": [
{"name": "number", "type": "string"},
{"name": "type", "type": ["int", "null"]}
]
}
}
}]
}
步骤2 使用Avro编译器生成Java语言, 命令如下:
java -jar avro-tools-1.7.4.jar compile schema person.avro .
注意, 上面的命令运行时的当前路径是person.avro所在目录。
步骤3 使用Avro库提供的API来编写应用程序。 该例子创建一个Person对象, 先将该对象保存到文件example.txt中, 之后从文
件中读出并打印。
public class AvroExample {
static public void main(String[] argv) {
PhoneNumber phoneNumber1 = PhoneNumber.newBuilder()
.setNumber("15110241024")
.setType(0).build();
PhoneNumber phoneNumber2 = PhoneNumber.newBuilder()
.setNumber("01025689654")
.setType(1).build();
List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
phoneNumbers.add(phoneNumber1);
phoneNumbers.add(phoneNumber2);
Person person = Person.newBuilder()
.setName("Dong Xicheng")
.setEmail("dongxicheng@yahoo.com")
.setId(11111)
.setPhone(phoneNumbers).build();
File file = new File("person.txt");
try {
DatumWriter<Person> personDatumWriter = new SpecificDatumWriter<Person>(Person.class);
DataFileWriter<Person> dataFileWriter = new DataFileWriter<Person>(personDatumWriter);
dataFileWriter.create(person.getSchema(), file);
dataFileWriter.append(person);
dataFileWriter.close();
} catch(Exception e) {
System.out.println("Write Error:" + e);
}t
ry {
DatumReader<Person> userDatumReader = new SpecificDatumReader<Person>(Person.class);
DataFileReader<Person> dataFileReader = new DataFileReader<Person>(file, userDatumReader);
person = null;
while (dataFileReader.hasNext()) {
person = dataFileReader.next(person);
System.out.println(person);
}
} catch(Exception e) {
System.out.println("Read Error:" + e);
}
}
}
如果不想编译person.avro文件, 需要使用另外一套应用程序接口, 具体可参 考官方文档 。
Apache Avro最初是为Hadoop量身打造的RPC框架, 考虑到 稳定性 , YARN暂时采用Protocol Buffers作为序列化库, RPC仍
使用MRv1中的RPC, 而Avro则作为日志序列化库使用( 将在第8章介绍) 。 在YARN MapReduce中, 所有事件的序列化/反序列化
均采用Avro完成, 相关定义在Events.avpr文件中, 举例如下:
{"namespace": "org.apache.hadoop.mapreduce.jobhistory",
"protocol": "Events",
"types": [
…{
"type": "record", "name": "JobInfoChange",
"fields": [
{"name": "jobid", "type": "string"},
{"name": "submitTime", "type": "long"},
{"name": "launchTime", "type": "long"}
]
},
{"type": "record", "name": "JobPriorityChange",
"fields": [
{"name": "jobid", "type": "string"},
{"name": "priority", "type": "string"}
]
},
{"type": "record", "name": "JobStatusChanged",
"fields": [
{"name": "jobid", "type": "string"},
{"name": "jobStatus", "type": "string"}
]
},
…]
}
参见网址http://code.google.com/p/protobuf/。
可参考第三方开源实现, 网址为http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns。
参见网址http://avro.apache.org/。
参见网址http://avro.apache.org/docs/current/gettingstartedjava.html。
YARN项目启动时, Apache Avro尚不成熟, 存在各种问题。
页:
[1]