浅谈Protobuf

最近在学习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

protobuf是将类的定义使用.proto文件进行描述,然后通过protoc.exe编译器,根据.proto自动生成.java文件,然后将.java文件拷贝到项目中使用即可,但是生成的.java文件中需要protobuf的jar包支持,不然拷贝到项目中会报缺少依赖的问题(当然也可以根据.proto文件生成.jar文件),我已经将protoc.exe和protobuf的框架jar都打成zip包,可以去url: http://download.csdn.net/detail/tlqfreedom/9731945下载,如果没有积分的可以留言。
解压的结果如下:

其中protoc.exe为编译器,protobuf-java-2.5.0.jar为框架jar包。

  • 编写.proto文件
本次例子中使用Product和Order来讲解使用,Product是产品,Order是订单。定义两个.proto文件,分别如下:
Product.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。
import用来导入该类型中对其他类型的依赖,本例中Order中包含Product,不过需要注意的由于本例中Product.proto和Order.proto在同一个目录下,所以不需要写成import "demo.Product.proto",不然会报错的。
option java_package="demo.tlq"用来指定生成的java类的包名,如果未指定,那么就是用package.
option java_outer_classname="OrderProto"用来指定生成的java类的类名。
message相当于c语言中的struct语句,表示定义一个信息,其实也就是类。
声明的字段有三种修饰符required,optional,repeated,其中required表示该字段必须被赋值,不然在编码和解码的时候会报异常,optional表示字段是可赋值可不赋值,一般后台服务框架在升级时,对新增字段都设置为optional,做到系统的兼容性,repeated表示该字段是List类型或者数组类型。
其中后面的required int32 id=1;为字段编码值,1-15性能最高,所以把数组都设置为1-15。

关于支持的字段类型如下:

      

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文件

使用protoc.exe进行编译,如果对protoc.exe命令不了解的话,可以使用protoc --help来查一下。


然后看一下src文件夹下,会看到生成的两个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

关于框架的使用图如下:


3.protobuf和其他的序列化框架的比较

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值