protocol-buffers初使用

什么是ProtoBuffers

ProtoBuf(Protocol Buffers)是一种跨平台、语言无关、可扩展的序列化结构数据的方法,可用于网络数据交换及存储。一般简称pb
一旦定义了要处理的数据的数据结构之后,就可以利用ProtoBuf的代码生成工具生成相关的代码。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或从各种不同流中对你的结构化数据轻松读写。

为什么是 ProtoBuffers

ProtoBuf 现在是 Google 用于数据交换和存储的通用语言。谷歌代码树中定义了 48162 种不同的消息类型,包括 12183 个 .proto 文件。它们既用于 RPC 系统,也用于在各种存储系统中持久存储数据。

  • 性能数据(和json对比)
    [图片上传失败...(image-73a327-1663947957237)]
    [图片上传失败...(image-9bfaf5-1663947957237)]
    序列化时间效率对比:
数据格式 1000条数据 5000条数据
Protobuf 195ms 647ms
Json 515ms 2293ms

序列化空间效率对比:

数据格式 5000条数据
Protobuf 22MB
Json 29MB

优点

  • 效率高
    从序列化后的数据体积角度,与XML、JSON这类文本协议相比,ProtoBuf通过T-(L)-V(TAG-LENGTH-VALUE)方式编码,不需要", {, }, :等分隔符来结构化信息,同时在编码层面使用varint压缩,所以描述同样的信息,ProtoBuf序列化后的体积要小很多,在网络中传输消耗的网络流量更少,进而对于网络资源紧张、性能要求非常高的场景,ProtoBuf协议是不错的选择。
    从序列化/反序列化速度角度,与XML、JSON相比,ProtoBuf序列化/反序列化的速度更快,比XML要快20-100倍。
  • 支持跨平台、多语言
    ProtoBuf是平台无关的,无论是Android与PC,还是C#与Java都可以利用ProtoBuf进行无障碍通讯。
  • 扩展性、兼容性好
    具有向后兼容的特性,更新数据结构以后,老版本依旧可以兼容,这也是ProtoBuf诞生之初被寄予解决的问题。因为编译器对不识别的新增字段会跳过不处理。

proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#。

  • 使用简单
    ProtoBuf 提供了一套编译工具,可以自动生成序列化、反序列化的样板代码,这样开发者只要关注业务数据idl,简化了编码解码工作以及多语言交互的复杂度。

缺点

  • 可读性差,缺乏自描述
    XML,JSON是自描述的,而ProtoBuf则不是。

ProtoBuf是二进制协议,编码后的数据可读性差,如果没有idl文件,就无法理解二进制数据流,对调试不友好。

如何使用pb

  • 在项目的根build.gradle配置如下
dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19'
}

plugins {
     ...
    id 'com.google.protobuf' version '0.8.19' apply false
}
  • 在modele下build.gradle中配置如下:
apply plugin: 'com.google.protobuf'

android {
    sourceSets {
        main {
            // 定义proto文件目录
            proto {
                srcDir 'src/main/proto'
                include '**/*.proto'
            }
        }
    }
}

dependencies {
    implementation 'com.google.protobuf:protobuf-java:3.17.2'
}

protobuf {
    //配置protoc编译器
    protoc {
      //  不支持 mac m1 arg使用x86_64
        if (osdetector.os == "osx") {
            //区分平台
            artifact = 'com.google.protobuf:protoc:3.17.2:osx-x86_64'
        } else {
            artifact = 'com.google.protobuf:protoc:3.17.2'
        }
    }
    //这里配置生成目录,编译后会在build的目录下生成对应的java文件
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                remove java
            }
            task.builtins {
                java {}
            }
        }
    }
}   

plugins {
    id 'com.google.protobuf'
}

android {
    sourceSets {
        main {
            // 定义proto文件目录
            proto {
                srcDir 'src/main/proto'
                include '**/*.proto'
            }
        }
    }
}

dependencies {
    implementation 'com.google.protobuf:protobuf-java:3.17.2'
}
protobuf {
    //配置protoc编译器
    protoc {
//  不支持 mac m1 arg使用x86_64
        if (osdetector.os == "osx") {
            //区分平台
            artifact = 'com.google.protobuf:protoc:3.17.2:osx-x86_64'
        } else {
            artifact = 'com.google.protobuf:protoc:3.17.2'
        }
    }
    //这里配置生成目录,编译后会在build的目录下生成对应的java文件
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                remove java
            }
            task.builtins {
                java {}
            }
        }
    }
}
  • 在model的main文件下建proto目录,并在下面建.proto文件
    image.png

    Person.proto
//pb版本
syntax = "proto3";
//存放位置
option java_package = "com.mysiga.netsocket";
//输出类名
option java_outer_classname = "Person";
//引入类
import "Adress.proto";
//定义类
message _Person{
//  定义字段为name的字段,数据为字符串
  string name = 1;
  int32 id = 2;
  string email = 3;
  bool isCheck = 4;
//  枚举
  enum _PhoneType{
    MOBILE=0;
    HOME=1;
    WORK=2;
  }
//  内部类
  message _PhoneNumber{
    string number=1;
    _PhoneType type=2;
  }
  string card=5;
//  集合
  repeated _PhoneNumber phone=6;
  _Adress adress=7;
}

Adress.proto

syntax="proto3";
option java_package="com.mysiga.netsocket";
option java_outer_classname="Adress";
message _Adress{
  string address=1;
}
  • [Make Project]编译下项目
    会在app/build/intermediates/javac/debug/包名/下生成相应class文件


    image.png
  • 项目中调用
    MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val phoneNumber=Person._Person._PhoneNumber.newBuilder().setNumber("1883883")
        val personBuild=Person._Person.newBuilder().setCard("汽车")
.setEmail("[email protected]").setIsCheck(true).addPhone(phoneNumber).setName("Wilson")
        val person=personBuild.build()
        Log.i("Wilson","name ${person}")
//        序列化
        val bytes=person.toByteArray()
//        反序列化
        try {
            val person1=Person._Person.parseFrom(bytes)
            Log.i("Wilson","name:${person1}")
            Log.i("Wilson","card:${person1.card}")
        } catch (e: InvalidProtocolBufferException) {
            e.printStackTrace()
        }
    }
}

总结

对于数据交互频繁且数据量大,建议使用pb,对于游戏或在线教育白板业务场景下基本都是用pb。只是可读性差点。如果数据量不大可用Json可读性高。以上就是简单实用。复杂实用还要深研究下官方文档了。

扩展资料

  • protocol-buffers官方文档
  • 附语言类型对照表
.proto Type Notes C++ Type Java Type Python Type[2] Go Type Ruby Type C# Type PHP Type
double double double float float64 Float double float
float float float float float32 Float float float
int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
uint32 使用变长编码 uint32 int int/long uint32 Fixnum 或者 Bignum(根据需要) uint integer
uint64 使用变长编码 uint64 long int/long uint64 Bignum ulong integer/string
sint32 使用变长编码,这些编码在负值时比int32高效的多 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
sint64 使用变长编码,有符号的整型值。编码时比通常的int64高效。 int64 long int/long int64 Bignum long integer/string
fixed32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 uint32 int int uint32 Fixnum 或者 Bignum(根据需要) uint integer
fixed64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 uint64 long int/long uint64 Bignum ulong integer/string
sfixed32 总是4个字节 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
sfixed64 总是8个字节 int64 long int/long int64 Bignum long integer/string
bool bool boolean bool bool TrueClass/FalseClass bool boolean
string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 string String str/unicode string String (UTF-8) string string
bytes 可能包含任意顺序的字节数据。 string ByteString str []byte String (ASCII-8BIT) ByteString string

你可能感兴趣的:(protocol-buffers初使用)