最近在学习Netty,通信层框架就讲到了序列化和反序列化的框架,其中就谈到了Protobuf,结合官网上的讲解(官网URL:https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation),总结一些自己的理解,protobuf的源码在github上的地址为:URL:https://github.com/google/protobuf。本次都是针对java平台做讲解。
1.protobuf的优势
- 跨语言跨平台的序列化框架,支持目前绝大多数语言例如C++、C#、PHP、pthyon等,可以跨异构系统。
- google内部系统几乎都是使用protobuf,性能比较靠,可靠性要好,大厂质量有保证
- 相比java的本省的序列化,码流要小,速度要快,相比Json字符串,protobuf是字节的,故大小要小。
- 相比于json,java解析要更简单。
- 使用protobuf编译器能自动生成代码,可能前期需要一点学习成本,但是掌握后,工作效率还是蛮高的。
2.如何使用protobuf
- 编写.proto文件
package demo;
option java_package="demo.tlq";
option java_outer_classname = "ProductProto";
message Product{
required int32 id = 1;
optional string name = 2;
required int32 price = 3;
}
Order.proto
package demo;
import "Product.proto";
option java_package="demo.tlq";
option java_outer_classname = "OrderProto";
message Order{
required int32 id=1;
required int32 total=2;
repeated Product product=3;
}
package解决命名空间的问题,类似java中的package。
protobuf 数据类型 | 描述 | 打包 | C++语言映射 |
bool | 布尔类型 | 1字节 | bool |
double | 64位浮点数 | N | double |
float | 32为浮点数 | N | float |
int32 | 32位整数、 | N | int |
uin32 | 无符号32位整数 | N | unsigned int |
int64 | 64位整数 | N | __int64 |
uint64 | 64为无符号整 | N | unsigned __int64 |
sint32 | 32位整数,处理负数效率更高 | N | int32 |
sing64 | 64位整数 处理负数效率更高 | N | __int64 |
fixed32 | 32位无符号整数 | 4 | unsigned int32 |
fixed64 | 64位无符号整数 | 8 | unsigned __int64 |
sfixed32 | 32位整数、能以更高的效率处理负数 | 4 | unsigned int32 |
sfixed64 | 64为整数 | 8 | unsigned __int64 |
string | 只能处理 ASCII字符 | N | std::string |
bytes | 用于处理多字节的语言字符、如中文 | N | std::string |
enum | 可以包含一个用户自定义的枚举类型uint32 | N(uint32) | enum |
message | 可以包含一个用户自定义的消息类型 | N | object of class |
N 表示打包的字节并不是固定。而是根据数据的大小或者长度。
例如int32,如果数值比较小,在0~127时,使用一个字节打包。
关于枚举的打包方式和uint32相同。
关于message,类似于C语言中的结构包含另外一个结构作为数据成员一样。
关于 fixed32 和int32的区别。fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.
- 编译成.java文件
- 将.java文件拷贝到项目中,已经导入protobuf-java-2.5.0.jar解决依赖jar包的问题
- 关于protobuf使用的api的介绍
package demo.tlq;
import java.util.ArrayList;
import java.util.List;
import com.google.protobuf.InvalidProtocolBufferException;
public class Test {
private static OrderProto.Order createOrder() {
OrderProto.Order.Builder builder = OrderProto.Order.newBuilder();
builder.setId(1);
int total = 0;
List<ProductProto.Product> list = new ArrayList<ProductProto.Product>(
2);
ProductProto.Product.Builder pb = ProductProto.Product.newBuilder();
pb.setId(1);
pb.setName("coffee");
pb.setPrice(11);
total += pb.getPrice();
list.add(pb.build());
pb.clear();
pb.setId(2);
pb.setName("chocolate");
pb.setPrice(15);
total += pb.getPrice();
list.add(pb.build());
builder.setTotal(total);
builder.addAllProduct(list);
return builder.build();
}
private static byte[] encode(OrderProto.Order order) {
return order.toByteArray();
}
private static OrderProto.Order decode(byte[] body)
throws InvalidProtocolBufferException {
return OrderProto.Order.parseFrom(body);
}
public static void main(String[] args)
throws InvalidProtocolBufferException {
OrderProto.Order order = createOrder();
System.out.println("Before encode : " + order.toString());
OrderProto.Order order2 = decode(encode(order));
System.out.println("After decode : " + order2);
System.out.println("Asset equals : " + order.equals(order2));
}
}
执行的结果如下:
Before encode : id: 1
total: 26
product {
id: 1
name: "coffee"
price: 11
}
product {
id: 2
name: "chocolate"
price: 15
}
After decode : id: 1
total: 26
product {
id: 1
name: "coffee"
price: 11
}
product {
id: 2
name: "chocolate"
price: 15
}
Asset equals : true
关于框架的使用图如下: