全称: Protocol Buffer
类似功能产品或框架: Apache Thrift, CORBA, Web Service
主要作用: 跨平台、跨语言交互、通信
核心:序列、反序列化
特点:小、快、简单
这东西由Google出品,08年的时候Google把它开源了,官方支持C++,JAVA和Python三种语言,但是由很多第三方的库来支持其他语言。RPC (Remote Procedure Call) 也由第三方插件。
这东西实现的功能说穿了其实很简单:
1. 你必须提供一个供交互双方使用的数据类型的接口文件; (.proto文件)
2. Protocol buffer 会帮你根据该文件生成某语言的Proto类
a. 该类是数据类型对于该语言的一个封装,如Java就是一个class,包含了各种属性可以set, get
b. 利用该类可以很方便的进行读写交互,如Java,就可以直接调xxx.build().writeTo(OutputStream stream) 写到二进制文件中去
3. 发送方用protocol buffer生成二进制,接收方用protocol buffer解析成其对应的数据模型,从而实现交互
看上去挺像XML的,不是么?REST?
1. 客户端将需要的数据,封装成XML的样子发送;
2. 服务器端接受XML, 解析后用JAXB转成Java对象,并覆上值交给后面对应方法处理
事实上二者本来就可以相互替换,因为他们提供的功能非常类似。但不同的是,protocol buffer更轻,而且中间介质是二进制,所以速度更快。
Java例子:
环境:win7 32bit
- 从官网下载protocol的编译器,如protoc-2.5.0-win32.zip
- 解开来后放到自建目录protobuffer/bin/目录下,并把该目录加到系统PATH中,这样可以直接在命令行使用protoc命令
- 生成一个Maven项目:
mvn archetype:generate -DgroupId=com.edi.poc -DartifactId=protobuf-test -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
- 加入protobuffer的dependency
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
- 在src下写一个.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;
}
- 用protoc
命令根据该proto文件生成Java类文件
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
实际命令:
protoc -I=. --java_out=main/java addressbook.proto
- 然后就可以看到main/java/com/example/tutorial下产生了AddressBookProtos.java文件
- 好了,现在创建一个工具类来写,一个工具类来读
写类:
package com.edi.poc;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import com.example.tutorial.AddressBookProtos.Person.PhoneType;
public class AddPerson {
// This function fills in a Person message based on user input.
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){
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();
phoneNumber.setNumber(number);
stdout.print("Is this a mobile, home, or work phone?");
String type = stdin.readLine();
switch(type)
{
case "home":
case "HOME":
phoneNumber.setType(PhoneType.HOME);
break;
case "mobile":
case "MOBILE":
phoneNumber.setType(PhoneType.MOBILE);
break;
case "work":
case "WORK":
phoneNumber.setType(PhoneType.WORK);
break;
default:
stdout.println("Unknown phone type. Using default.");
}
person.addPhone(phoneNumber);
}
return person.build();
}
public static void main(String[] args) throws Exception {
if(args.length!=1){
System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE");
System.exit(-1);
}
AddressBook.Builder addressBook = AddressBook.newBuilder();
try {
addressBook.mergeFrom(new FileInputStream(args[0]));
} catch (FileNotFoundException e) {
System.out.println(args[0] + ": File not found. Create a new file.");
} catch (IOException e) {
e.printStackTrace();
}
addressBook.addPerson(PromptForAddress(new BufferedReader(new InputStreamReader(System.in)), System.out));
FileOutputStream output = new FileOutputStream(args[0]);
addressBook.build().writeTo(output);
output.close();
}
}
读类:
package com.edi.poc;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
public class ListPeople {
static void print(AddressBook addressBook){
for(Person p: addressBook.getPersonList())
{
System.out.println("Person ID: " + p.getId());
System.out.println(" Name: " + p.getName());
if(p.hasEmail())
System.out.println(" E-mail: " + p.getEmail());
for(Person.PhoneNumber num:p.getPhoneList())
{
switch(num.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;
default:
}
System.out.println(num.getNumber());
}
}
}
public static void main(String[] args) throws FileNotFoundException, IOException {
if(args.length!=1){
System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE");
System.exit(-1);
}
AddressBook addressBook = AddressBook.parseFrom(new FileInputStream(args[0]));
print(addressBook);
}
}
- 自己执行AddPerson类,写入一个文件,然后ListPeople类可以将之前写的读出来。
这个例子是读写一个文件,实际应用中,可以把输出流放到socket,进行直接交互,或者REST -> 关键字@Produce("application/x-protobuf")