美文网首页
google序列化框架protobuf实践

google序列化框架protobuf实践

作者: 北交吴志炜 | 来源:发表于2019-08-18 14:58 被阅读0次

前言

暴雪出品,必属精品,google亦然。对象序列化方式有很多,个人而言,java自带序列化ObjectOutputStream/ObjectOutputStream,我只是写过一些UT,实际商业项目中从没用过。Json用的最多,而且Json现在的流行度跟很早之前的XML一样都很高,各种rpc框架基本都会支持。最近遇到一个java 调用c++服务的项目,用到了google的protobuf,算是我的一个盲区,写一篇文章记录一下学习过程。主要参见官网文档及demo https://developers.google.cn/protocol-buffers/

protobuf 是什么

Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data,think XML, but smaller, faster, and simpler.

官网介绍简明扼要,protobuf就是一个序列化框架,语言无关,平台无关,可拓展;比XML性能更好更易用。一次定义,到处运行。

protobuf使用流程

Define message formats in a .proto file.
Use the protocol buffer compiler.
Use the Java protocol buffer API to write and read messages.

1.定义一个.proto文件
2.使用protobuf的编译器对.proto文件进行编译,生成语言相关的类。
3.使用生成的类,通过protobuf api进行读写

以官方demo中的addressbook.proto文件为例

syntax = "proto2"; //定义protobuf版本,可以是proto2或者proto3

package tutorial;

option java_package = "com.example.tutorial"; //输出类文件的package
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 phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

.proto文件的核心应该是message的定义,类似C语言的结构体。
比如上例,Person有四个属性,name,id,email,phones

protobuf内置了bool, int32, float, double, and string等基本类型,大眼一看也能知道对应java的什么数据类型。属性也支持非基本类型之外的复杂结构,比如PhoneNumber phones属性,他本身也是一个message结构体,跟java的对象属性一样。

注意的点,上述.proto文件中定义的属性后面都有 =1,=2,=3,=4 这些,这不是属性初始化的默认值,这是属性的tag,是一个属性的标识,是一个非常关键的概念,这个tag主要是用来支持.proto文件的拓展。比如我们server进行了升级,加了字段,那么我们的consumer端,是不是一定也要同步升级呢?其实不用,consumer忽略你加的字段即可,这个特性,与这个tag是强相关的。其实现原理是,序列化时,protobuf将属性存为一个键值对,这个key,即属性的tag号,那么反序列化的时候,consumer就可以忽略不识别的key,只解析那些自己识别的key。

上述.proto文件定义好之后,就可以使用protobuf的编译器protoc对文件进行编译了

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

这个命令只要三个参数,.proto文件目录,输出类文件目录,.proto文件全路径
比如我本机

protoc -I=/Users/wuzhiwei/tianchi/javacase/java_case/src/main/java/protobuf --java_out=/Users/wuzhiwei/tianchi/javacase/java_case/src/main/java /Users/wuzhiwei/tianchi/javacase/java_case/src/main/java/protobuf/addressbook.proto

执行成功之后,会在输出类文件目录中生成对应的类文件,AddressBookProtos.java
生成的代码太长就不贴了,跟thrift类似,都是固定的套路对属性生成get,set方法,以及其他的一些api,感兴趣完整版又不愿动手的可以到我的git上面拿代码https://github.com/somewaters/java_case/tree/master/src/main/java/protobuf

有一点,生成的文件特别提到了,生成的类文件不要edit,这个说的其实是不要改变那些与.proto文件对应的属性和方法,本质上其实这还是一个java类文件,你可以在他生成的类文件中加入自己想要的方法或者属性,并不影响功能正常使用。

// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: addressbook.proto

类文件生成完毕,protobuf api有两个关键的方法,toByteArray,parseFrom分别来支持对象序列化和反序列化

public byte[] toByteArray() {
        try {
            byte[] result = new byte[this.getSerializedSize()];
            CodedOutputStream output = CodedOutputStream.newInstance(result);
            this.writeTo(output);
            output.checkNoSpaceLeft();
            return result;
        } catch (IOException var3) {
            throw new RuntimeException(this.getSerializingExceptionMessage("byte array"), var3);
        }
    }
public static protobuf.AddressBookProtos.AddressBook parseFrom(byte[] data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data);
    }

同样的完整例子可以看之前我的git代码。
使用层面的话,protobuf api还是很丰富的,我只关注与对象跟byte[]之前的转化。

注意的点,之前提过,protobuf支持可拓展,但是这个拓展是有前提的。

you must not change the tag numbers of any existing fields.
you must not add or delete any required fields.
you may delete optional or repeated fields.
you may add new optional or repeated fields but you must use fresh tag numbers (i.e. tag numbers that were never used in this protocol buffer, not even by deleted fields).

1.不能改变属性的tag值。(就是之前.proto文件中定义属性的时候=1,=2那些值)
2.required修饰的属性不能加或者删
2.optional or repeated修饰的属性可以删
4.optional or repeated修饰的属性可以加,但是必须使用全新的tag号。

总结:

语言无关,平台无关,依赖的是二进制流先天的跨平台,跨语言,protobuf提供不同语言,不同平台的的api负责将对象序列化为byte[]或者将byte[]反序列化即可;
一次编写,到处运行,protobuf定义好.proto文件后,在所有地方,都是通过其compiler 自动生成的类文件,编写一次.proto文件即可。
可拓展,.proto文件在遵循拓展规则之后,可以做到兼容升级,老版本忽略新版本的增加的内容即可。
所有的序列化框架,做的都是同样的事情,即将对象序列化为byte[],或者反之。那么为什么protobuf就敢号称faster呢?后续如果有必要,会继续研究其在性能上取胜到底依赖的是什么。

相关文章

  • google序列化框架protobuf实践

    前言 暴雪出品,必属精品,google亦然。对象序列化方式有很多,个人而言,java自带序列化ObjectOutp...

  • Protobuf的介绍和使用

    一、介绍 Protocol Buffers(简称Protobuf) ,是Google出品的序列化框架,与开发语言无...

  • REST协议改造gRPC实战

    gRPC 是由 Google 主导开发的 RPC 框架,使用 HTTP/2 协议并用 ProtoBuf 作为序列化...

  • Golang RPC 之 gRPC

    gRPC 简介: gRPC 是一款高性能、开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化...

  • Python RPC 之 gRPC

    gRPC 简介: gRPC 是一款高性能、开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化...

  • go 微服务(3) gRPC

    RPC gRPC 是由 Google 主导开发的 RPC 框架,使用HTTP/2协议并用ProtoBuf作为序列化...

  • 谈谈Grpc做服务化

    Grpc的介绍 Grpc是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工...

  • grpc java 简单配置 及实现流程

    gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。其客户端提供...

  • gRPC初体验

    gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。其客户端提供...

  • 转载:Protobuf在Cmake中的正确使用

    Protobuf在Cmake中的正确使用 Protobuf是google开发的一个序列化和反序列化的协议库,我们可...

网友评论

      本文标题:google序列化框架protobuf实践

      本文链接:https://www.haomeiwen.com/subject/jtersctx.html