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("121431@qq.com").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可读性高。以上就是简单实用。复杂实用还要深研究下官方文档了。

扩展资料

.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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 定义一个消息类型指定字段类型分配标识号指定字段规则添加更多消息类型添加注释保留标识符(Reserved)从.pro...
    MrChenyz阅读 2,380评论 0 0
  • 什么是 Protobuf Protobuf是Protocol Buffers的简称,它是Google公司开发的一种...
    秋枫残红阅读 511评论 0 0
  • 英文原文:Language Guide (proto3)[https://developers.google.co...
    岑吾阅读 430评论 0 6
  • 序列化 计算机中最原始的数据就是0和1,有电才能驱动计算机运行,1和0对应电的正负,易经有太极生两仪,两仪生四象,...
    面向星辰大海的程序员阅读 265评论 0 0
  • protobuf 复习记录 创建时间一天三天十五三十天2021.7.20--------------- 目录 描述...
    kobeyyf阅读 533评论 0 0