ProtocolBuffer
在HBase中引进了Protocol Buffer作为序列化/反序列化引擎使用到RPC,所以有必要来了解ProtocolBuffer。
protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。
由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
一个proto文件的定义是:
“option java_package = "org.ibm.developerworks";
option java_outer_classname = "getRowCount";
option java_generic_services = true;
option optimize_for = SPEED;
message getRowCountRequest{
required bool reCount = 1;
}
message getRowCountResponse {
optional int64 rowCount = 1;
}
service ibmDeveloperWorksService {
rpc getRowCount(getRowCountRequest)
returns(getRowCountResponse);
}”
对于文件中的字段格式为:
限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
字段的修饰符:required optional repeated
required:信息的发送方必须编写该字段,接收方能够解析
optional:该字段不是必须的,且对于接受方识别是可选的,能识别则处理,不能识别则忽略字段。
repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。
message,类似于C语言中的结构包含另外一个结构作为数据成员一样。
protoc -I=F:\EditPlus --java_out=F:\1 F:\EditPlus\test_Pro.proto ---proto编译命令
-I等号右边表示的是待编译文件所在的位置, --java_out右边的等号表示文件编译之后输出路径
F:\EditPlus\test_Pro.proto 是proto文件的路径
protoc --version 查看protocBuffer版本
在基本了解PB之后,实践编程直观的看到PB的强大。通过实例化编译完成的Java实体类,向文件中写入和从文件中读取方式。
1、编写.proto文件
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person{
required string name = 1;
required int32 id =2;
optional string email = 3;
enum PhoneType{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber{
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook{
repeated Person person = 1;
}
文件内容简单解释:定义了AddressBookProtos类,类所在的包名是com.example.tutotial 。message中定义了消息的数据格式,包含变量类型和变量名。
打开DOS环境输入编译.proto文件命令:protoc -I=F:\EditPlus --java_out=F:\1 F:\EditPlus\test_Pro.proto
可在F盘的文件夹1中找到对应生成的Java文件,注意点:
文件生成对应输出的文件夹必须存在,否则编译不成功;生成的Java文件与proto文件的命名无关;编译的文件不要修改。
2.创建Java工程,将编译完成的Java文件拷贝到该工程下。
3.编写在基于该PB定义的信息写。采用的方式是:从控制台将所需信息输入,写到对应的文件中。在src目录下创建文件夹Book,创建文件TestPerson.book
public class AddPerson {
/**
* 将用户输入的Person message写入输出流中
*
* @param stdin
* 输入流
* @param stdout
* 打印输出流
* @return Person类
* @throws IOException
*/
static Person PromptForAddress(BufferedReader stdin, PrintStream stdout)
throws IOException {
Person.Builder person = Person.newBuilder();
stdout.print("Enter person ID:");
person.setId(Integer.valueOf(stdin.readLine()));
stdout.print("Enter name: ");
person.setName(stdin.readLine());
// 空白表示没有
stdout.print("Enter email address (blank for none):");
String email = stdin.readLine();
if (email.length() > 0) {
person.setEmail(email);
}
while (true) {
// 按下Enter键结束输入
stdout.print("Enter a phone number (or leave blank to finish):");
String number = stdin.readLine();
if (number.length() == 0) {
break;
}
Person.PhoneNumber.Builder phoneNumber = Person.PhoneNumber.newBuilder().setNumber(
number);
// 输入完成之后需要确定你输入的是手机号、家庭电话还是工作电话
stdout.print("Is this a mobile, home, or work phone? ");
String type = stdin.readLine();
if (type.equals("mobile")) {
phoneNumber.setType(Person.PhoneType.MOBILE);
}
else if (type.equals("home")) {
phoneNumber.setType(Person.PhoneType.HOME);
}
else if (type.equals("work")) {
phoneNumber.setType(Person.PhoneType.WORK);
}
else {
stdout.println("Unknown phone type.Using default.");
}
person.addPhone(phoneNumber);
}
return person.build();
}
public static void main(String[] args)
throws Exception {
AddressBook.Builder addressBook = AddressBook.newBuilder();
// 检验是否存在这个文件
try {
addressBook.mergeFrom(new FileInputStream("src/Book/TestPerson.book"));
}
catch (FileNotFoundException e) {
System.out.println("src/Book/TestPerson.book"
+ ": File not found.Creating a new file.");
}
// 将这条Person message添加到AddressBook中
addressBook.addPerson(PromptForAddress(
new BufferedReader(new InputStreamReader(System.in)), System.out));
// 将新建的AddressBook写入文件当中
FileOutputStream output = new FileOutputStream("src/Book/TestPerson.book");
addressBook.build().writeTo(output);
output.close();
}
}
运行程序,并在控制台输入相关属性的信息,查看TestPerson.book可以看到输入的信息已在文件中。
4.从文件中读取写入的信息。
public class ListPeople {
/**
* 迭代遍历并且打印文件中所包含的信息
*
* @param addressBook
* AddressBook对象
*/
static void Print(AddressBook addressBook) {
for (Person person : addressBook.getPersonList()) {
System.out.println("Person ID: " + person.getId());
System.out.println("Name: " + person.getName());
if (person.hasEmail()) {
System.out.println("E-mail address:" + person.getEmail());
}
for (Person.PhoneNumber phoneNumber : person.getPhoneList()) {
switch (phoneNumber.getType()) {
case MOBILE:
System.out.print("Mobile phone #: ");
break;
case HOME:
System.out.print("Home phone #: ");
break;
case WORK:
System.out.print("Work phone #: ");
break;
}
System.out.println(phoneNumber.getNumber());
}
}
}
public static void main(String[] args)
throws Exception {
// 读取已经存在.book文件
AddressBook addressBook = AddressBook.parseFrom(new FileInputStream(
"src/Book/TestPerson.book"));
Print(addressBook);
}
}
在控制台中可以看到在文件中的信息被读出来。
简单的看是文件的写入与读入,但是其实依赖的是用PB定义的消息格式,包含了信息的定义与解析。这里只是展示了都用Java的实现。此处也可以用python来编译解析,不用修改.proto文件。