一文带你玩转ProtoBuf

这篇具有很好参考价值的文章主要介绍了一文带你玩转ProtoBuf。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,在微服务架构中通常使用另外一个数据交换的协议的工具ProtoBuf。

ProtoBuf也是我们做微服务开发,进行Go进阶实战中,必知必会的知道点。

今天就开始第一章内容:《一文带你玩转ProtoBuf》

5分钟入门

1.1 简介

你可能不知道ProtoBuf,但一定知道json或者xml,从一定意义上来说他们的作用是一样的。

ProtoBuf全称:protocol buffers,直译过来是:“协议缓冲区”,是一种与语言无关、与平台无关的可扩展机制,用于序列化结构化数据。

和json\xml最大的区别是:json\xml都是基于文本格式,ProtoBuf是二进制格式。

ProtoBuf相比于json\XML,更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

我们只需要定义一次数据结构,就可以使用ProtoBuf生成源代码,轻松搞定在各种数据流和各种语言中写入、读取结构化数据。

1.2 安装

建议大家使用主流版本v3,这是官网下载地址:https://github.com/protocolbuffers/ProtoBuf/releases

注意,不同的电脑系统安装包是不一样的:

  • Windows 64位 点这里下载

  • Windows 32位 点这里下载

  • Mac Intel 64位 点这里下载

  • Mac ARM 64位 点这里下载

  • Linux 64位 点这里下载

(公众号无法跳转到外链,点击文末的阅读原文可以跳转到下载地址。)

小技巧:Mac查看自己的芯片类型点击左上角的苹果图标,再点击关于本机,就可以查看了。

一文带你玩转ProtoBuf

比如,我的处理器芯片是intel的,下载安装包之后是这样的:

bin目录下的protoc是ProtoBuf的工具集,下文会重点介绍它的使用。

注意:我们需要将下载得到的可执行文件protoc所在的 bin 目录加到我们电脑的环境变量中。

Mac安装小技巧

如果你的Mac安装了brew,安装ProtoBuf就更简单了,我们使用brew install ProtoBuf就可以了

1.3 编译go语言的工具包

这个protoc可以将proto文件编译为任何语言的文件,想要编译为go语言的,还需要下载另外一个可执行文件

命令是这样的:

go install google.golang.org/ProtoBuf/cmd/protoc-gen-go@latest

1.4 编写proto代码

下面就编写一个非常简单,但是五脏齐全的proto代码,我们再根据这段代码生成pb.go文件。

syntax = "proto3";

package hello;

option go_package = "./;hello";

message Say{
  int64           id    = 1;
  string          hello = 2;
  repeated string word  = 3;
}

1.5 生成go代码

生成go代码,非常简单,使用下面的命令就可以了。

切换到.proto文件所在目录

cd proto/demo/

指定proto源文件,自动生成代码。

protoc --go_out=. hello.proto

执行上面的命令后,我们在项目中就自动生成了一个.pb.go的文件

一文带你玩转ProtoBuf

入门ProtoBuf就是这么的简单:通过这几步我们就完成了ProtoBuf的下载、安装、编写了一个proto文件,并生成了能用Go语言读写ProtoBuf的源代码。

我们再深入了解一下probuf的用法:

10分钟进阶

下面再带大家深入了解一下ProtoBuf的知识点,避免在开发中踩坑。

小技巧:写proto和写go最大的区别是需要在结尾添加分号的;,在开发过程中给自己提个醒:如果是写proto需要加分号,如果是写go不需要加分号。

以我们上面的proto入门代码举例:

一文带你玩转ProtoBuf

1.1 关键字

  • syntax:是必须写的,而且要定义在第一行;目前proto3是主流,不写默认使用proto2

  • package:定义我们proto文件的包名

  • option go_package:定义生成的pb.go的包名,我们通常在proto文件中定义。如果不在proto文件中定义,也可以在使用protoc生成代码时指定pb.go文件的包名

  • message:非常重要,用于定义消息结构体,不用着急,下文会重点讲解

细心的小伙伴一定注意到了 message 消息体中有一个 “repeated” 关键字,这在我们写Go的时候是没有的。

这是干什么用的呢?下面来详细解答一下:

1.2 数组类型

关于数组类型,和Java、Go、PHP等语言中,定义数据类型不一样。

在ProtoBuf消息中定义数组类型,是通过在字段前面增加repeated关键词实现,标记当前字段是一个数组。

只要使用repeated标记类型定义,就表示数组类型。

我们来举两个例子:

1.整数数组:

下面定义的arrays表示int32类型的数组

message Msg {
  repeated int32 arrays = 1;
}

2.字符串数组

下面定义的names表示字符串数组

message Msg {
  repeated string names = 1;
}

repeated搞懂了,message又是干嘛用的呢?

1.3 消息

消息(message),在ProtoBuf中指的就是我们要定义的数据结构。类似于Go中定义结构体。

message关键词用法也非常简单:

1. 语法

syntax = "proto3";

message 消息名 {
    消息体
}

例子:

syntax = "proto3";
 
message Request {
  string query = 1;
  int32  page = 2;
  int32  limit = 3;
}

定义了一个Request消息,这个消息有3个字段,query是字符串类型,page和limit是int32类型。

1.4 字段类型

ProtoBuf支持多种数据类型,例如:string、int32、double、float等等,我整理了一份ProtoBuf和go语言的数据类型映射表

.proto Type Go Type 使用技巧
double float64 没特殊技巧,记住float对应go的float32,double对应go的float64就可以了
float float32 没特殊技巧,记住float对应go的float32,double对应go的float64就可以了
int32 int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代
uint32 uint32 使用变长编码
uint64 uint64 使用变长编码
sint32 int32 使用变长编码,这些编码在负值时比int32高效的多
sint64 int64 使用变长编码,有符号的整型值。编码时比通常的int64高效。
fixed32 uint32 总是4个字节,如果数值都比228大的话,这个类型会比uint32高效。
fixed64 uint64 总是8个字节,如果数值都比256大的话,这个类型会比uint64高效。
sfixed32 int32 总是4个字节
sfixed64 int64 总是8个字节
bool bool 严格对应,玩不出其他花样来
string string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
bytes []byte 可以包含任意顺序的字节数组

1.5 分配标识号

细心的小伙伴可能又有疑问了,上面消息体中的 string query = 1; 这个1是什么呢?

这些数字是“分配表示号”:在消息定义中,每个字段后面都有一个唯一的数字,这个就是标识号。

这些标识号的作用是:用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。

注意:分配标识号在每个消息内唯一,不同的消息体是可以拥有相同的标识号的。

小技巧:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。

1.5.1 保留标识号(Reserved)

小技巧:要为将来有可能添加的、频繁出现的字段预留一些标识号。

我们想保留一些标识号,留给以后用,可以使用下面语法:

message Test {
  reserved 2, 5, 7 to 10; // 保留2,5,7到10这些标识号
}

如果使用了这些保留的标识号,protocol buffer编译器无法编译通过,将会输出警告信息。

一文带你玩转ProtoBuf

1.6 将消息编译成各种语言版本的类库

编译器命令格式:

protoc [OPTION] PROTO_FILES

OPTION是命令的选项, PROTO_FILES是我们要编译的proto消息定义文件,支持多个。

常用的OPTION选项:

  --go_out=OUT_DIR            指定代码生成目录,生成 Go 代码
  --cpp_out=OUT_DIR           指定代码生成目录,生成 C++ 代码
  --csharp_out=OUT_DIR        指定代码生成目录,生成 C# 代码
  --java_out=OUT_DIR          指定代码生成目录,生成 java 代码
  --js_out=OUT_DIR            指定代码生成目录,生成 javascript 代码
  --objc_out=OUT_DIR          指定代码生成目录,生成 Objective C 代码
  --php_out=OUT_DIR           指定代码生成目录,生成 php 代码
  --python_out=OUT_DIR        指定代码生成目录,生成 python 代码
  --ruby_out=OUT_DIR          指定代码生成目录,生成 ruby 代码

因为开篇我们就用Go举了例子,下面再用Java举个例子吧:

protoc --java_out=. hello.proto

在当前目录导出java版本的代码,编译hello.proto消息,执行效果如下:

一文带你玩转ProtoBuf

下载再带小伙伴们了解一下ProtoBuf的进阶知识点吧:枚举类型、消息嵌套和Map类型。

1.7 枚举类型

写Java的同学枚举一定用的很溜,但是写Go的同学可能有点懵了,Go是不直接支持枚举的,并没有Enum关键字。

关注我,后续会详解Go枚举相关的知识点,在这篇文章中不做重点介绍。

使用枚举的场景是这样的:

当定义一个消息类型的时候,可能想为一个字段指定“预定义值”中的其中一个值,这时候我们就可以通过枚举实现,比如这种:

syntax = "proto3";//指定版本信息,非注释的第一行

enum SexType //枚举消息类型,使用enum关键词定义,一个性别类型的枚举类型
{
    UNKONW = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
    MALE = 1;  //1男
    FEMALE = 2; //2女  0未知
}

// 定义一个用户消息
message UserInfo
{
    string name = 1; // 姓名字段
    SexType sex = 2; // 性别字段,使用SexType枚举类型
}

运行效果如下:

一文带你玩转ProtoBuf

在实际开发中,我们需要定义很多的proto,我们如何做到消息的复用呢?

答案就是:“消息嵌套”

1.8 消息嵌套

我们在开发Java和PHP时,经常嵌套使用类,也可以使用其他类作为自己的成员属性类型;在开发Go时经常嵌套使用结构体。

在ProtoBuf中同样支持消息嵌套,可以在一个消息中嵌套另外一个消息,字段类型可以是另外一个消息类型。

我们来看下面3个经典示例:

1.8.1 引用其他消息类型的用法

// 定义Article消息
message Article {
  string url = 1;
  string title = 2;
  repeated string tags = 3; // 字符串数组类型
}

// 定义ListArticle消息
message ListArticle {
  // 引用上面定义的Article消息类型,作为results字段的类型
  repeated Article articles = 1; // repeated关键词标记,说明articles字段是一个数组
}

1.8.2 消息嵌套

类似类嵌套一样,消息也可以嵌套,比如这样:

message ListArticle {
  // 嵌套消息定义
  message Article {
    string url = 1;
    string title = 2;
    repeated string tags = 3;
  }
  // 引用嵌套的消息定义
  repeated Article articles = 1;
}

1.8.3 import导入其他proto文件定义的消息

我们在实际开发中,通常要定义很多消息,如果都写在一个proto文件,是不方便维护的。

小技巧:将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。

例子:

创建文件: article.proto

syntax = "proto3";

package nesting;

option go_package = "./;article";

message Article {
  string          url   = 1;
  string          title = 2;
  repeated string tags  = 3; // 字符串数组类型
}

创建文件: list_article.proto

syntax = "proto3";
// 导入Article消息定义
import "article.proto";

package nesting;

option go_package = "./;article";

// 定义ListArticle消息
message ListArticle {
  // 使用导入的Result消息
  repeated Article articles = 1;
}

执行效果如下,我们顺利生成了.pb.go文件:

一文带你玩转ProtoBuf

1.9 map类型

我们在Go语言开发中,最常用的就是切片类型和map类型了。

切片类型在ProtoBuf中对应的就是repeated类型,前面我们已经介绍过了。

再重点介绍一下map类型,ProtoBuf也是支持map类型的:

1.9.1 map语法

map<key_type, value_type> map_field = N;

语法非常简单和通用,但是有几个问题需要我们注意:

  1. key_type可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)。

  2. 注意:枚举不是有效的key_type

  3. value_type 可以是除另一个映射之外的任何类型。

  4. Map 字段不能使用repeated关键字修饰。

1.9.2 map的例子

我们举个典型的例子:学生的学科和分数就适合用map定义:

syntax = "proto3";

package map;

option go_package = "./;score";

message Student{
  int64              id    = 1; //id
  string             name  = 2; //学生姓名
  map<string, int32> score = 3;  //学科 分数的map
}

运行效果如下: 

一文带你玩转ProtoBuf

再强调一下

注意:Map 字段是不能使用repeated关键字修饰。

至此我们已经掌握了ProtoBuf的所有知识点,是不是非常简单清晰呢?

下面我们在Go项目中实战应用一下ProtoBuf,从ProtoBuf中读取数据,并且转换为我们常用的结构体

5分钟实战

1. 首先我们定义proto文件

我创建了一个demo目录,创建了名为study_info.proto的文件

syntax = "proto3";

package demo;

option go_package = "./;study";

message StudyInfo {
  int64              id       = 1; //id
  string             name     = 2; //学习的科目名称
  int32              duration = 3; //学习的时长 单位秒
  map<string, int32> score    = 4; //学习的分数
}

2. 生成代码

使用命令生成pb.go文件:

protoc --go_out=. study_info.proto

3.编写go文件

编写go文件,读取ProtoBuf中定义的字段,进行赋值,取值,转成结构体等操作:

proto编码和解码的操作和json是非常像的,都使用“Marshal”和“Unmarshal”关键字。

package main

import (
   "fmt"
   "google.golang.org/ProtoBuf/proto"
   study "juejin/ProtoBuf/proto/demo"
)

func main() {
   // 初始化proto中的消息
   studyInfo := &study.StudyInfo{}

   //常规赋值
   studyInfo.Id = 1
   studyInfo.Name = "学习ProtoBuf"
   studyInfo.Duration = 180

   //在go中声明实例化map赋值给ProtoBuf消息中定义的map
   score := make(map[string]int32)
   score["实战"] = 100
   studyInfo.Score = score

   //用字符串的方式:打印ProtoBuf消息
   fmt.Printf("字符串输出结果:%v\n", studyInfo.String())

   //转成二进制文件
   marshal, err := proto.Marshal(studyInfo)
   if err != nil {
      return
   }
   fmt.Printf("Marshal转成二进制结果:%v\n", marshal)

   //将二进制文件转成结构体
   newStudyInfo := study.StudyInfo{}
   err = proto.Unmarshal(marshal, &newStudyInfo)
   if err != nil {
      return
   }
   fmt.Printf("二进制转成结构体的结果:%v\n", &newStudyInfo)
}

运行结果如下:

一文带你玩转ProtoBuf

本文总结

ProtoBuf作为开发微服务必选的数据交换协议,基于二进制传输,比json/xml更小,速度更快,使用也非常的简单。

通过这篇文章,我们不仅学会了ProtoBuf的入门操作,还使用Go语言基于ProtoBuf编码解码了数据,进行了实战。

进阶部分带大家了解了ProtoBuf如何定义消息、ProtoBuf和Go数据类型的映射、枚举类型如何使用、通过消息嵌套复用代码、使用map类型时需要注意的问题和小技巧。

微服务架构成为企业项目的必然选择已是趋势,如果你只会开发单体项目,请关注我,带你一起玩转微服务。

天下难事,必作于易。想都是问题,做才有答案。站着不动,永远是观众。

关于专栏

近期会更新一系列Go实战进阶的文章,欢迎大家关注我的签约专栏 :Go语言进阶实战。

这是近期会更新文章的知识脉络图,感兴趣的小伙伴可以关注一波,欢迎日常催更。

一文带你玩转ProtoBuf 

已完成

《一文玩转ProtoBuf》

《开发gRPC总共分三步》

《Go WEB进阶实战:基于GoFrame搭建的电商前后台API系统》

小伙伴们还想看哪些内容,欢迎在评论区留言。文章来源地址https://www.toymoban.com/news/detail-414699.html

到了这里,关于一文带你玩转ProtoBuf的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 带你玩转双链表

    相信经过前面的学习,大家已经了解的单链表的缺陷和用途,今天我们学习双链表,和以前不同,今天双链表的实现我们增加一点点的难度,但我相信这些难度对大家都没有问题。和之前单链表的实现,我们的数据类型是固定的,主函数中传什么我们的单链表结构体中就需要

    2024年02月13日
    浏览(34)
  • 带你玩转单向链表(学习必备)

    本篇文章主要介绍数据结构中 单向链表 各种操作,适合有 C语言基础 的同学,文中描述和代码示例很详细,干货满满,感兴趣的小伙伴快来一起学习吧! ☀️大家好!我是新人博主朦胧的雨梦,希望大家多多关照和支持😝😝😝 🌖大家一起努力,共同成长,相信我们都会

    2024年02月02日
    浏览(45)
  • 带你玩转三子棋—【C语言】

    目录 前言: 1. 菜单的打印 2. game函数的实现 2.1 初始化棋盘 2.2 显示棋盘 2.3 玩家下棋 2.4 电脑下棋 2.5 判断输赢 2.6 判断棋盘是否满了 3. 全部代码 3.1 game.h 3.2  game.c 3.3 test.c 为了实现三子棋,首先我们应该将代码分模块编写,我们分为3个部分 1. test.c —测试游戏(主函数)2

    2024年02月04日
    浏览(43)
  • Python | 带你玩转Python的各种文件操作

    本篇文章主要介绍Python的各种文件操作,适合刚入门的小白或者对于文件操作基础不太牢固的同学,文中描述和代码示例很详细,看完即可掌握,感兴趣的小伙伴快来一起学习吧。 ☀️大家好!我是新人小白博主朦胧的雨梦,希望大家多多关照和支持😝😝😝 🌖大家一起努

    2023年04月11日
    浏览(67)
  • 【Linux】32条指令带你玩转 Linux !

    目录 1,whoami 2,who 3,pwd 4,ls 1,ls  2,ls -l 3,ls -a 4,ls -al 5,ls -d  6,ls -ld 5,clear 6,cd 1,cd  2,cd . 3,cd .. 4,cd /home/litao/linux/  绝对路径 5,cd ../day02/   相对路径 6,cd ~ 7,cd - 7,tree 8,touch 9,mkdir 1,mkdir 2,mkdir -p 10,rmdir rm 1,rmdir 2,rm 3,rm -f 4,rm -r 5,rm -rf 6,

    2024年02月08日
    浏览(52)
  • 【C语言】带你玩转库函数qsort

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,之前更新的一直是比较基础和简单的内容,随着博主自己的水平的提升,今天给大家带来点不一样的东西,我们今天要讲的是库函数qsort的用法 废话不多说,咱们直接开始吧! 很多人可能是

    2024年02月16日
    浏览(36)
  • 内网穿透技术 - 带你玩转Ngrok和NATAPP

    使用内网穿透技术,我们出差或者在家,就可以直接访问到公司的电脑或者内网网站,实现远程办公。也可以将我们自己在局域网内搭建的网站暴露出去,让所有人都可以直接访问到。 本文章主要介绍下内网穿透工具Ngrok和NATAPP。 Ngrok 开源,老牌穿透工具 NATAPP 免费隧道,提

    2024年02月15日
    浏览(34)
  • 带你玩转 Vite + Vue3 高低版本常用玩法

    Vite 是一种新型前端构建工具,在我们保险前端项目中已经推动并应用很久了,Vite 能够显著降低构建时间,提升前端开发效率。 它主要由两部分组成: 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR) 一套构建指令,它使

    2024年02月05日
    浏览(57)
  • 带你玩转 ui 框架 ——scoped及样式穿透问题详解

    前言 在我们前端的开发中经常会使用到各种 ui 框架 下面这两个是比较火的,也是我常用的两个ui框架。 问题描述 但是在使用框架的时候难免会遇到需要改变组件中的一些样式,当然如果我们所有页面的组件样式都是统一的话,我们可以进行全局设置样式,但是如果我们仅仅

    2023年04月21日
    浏览(41)
  • 带你玩转 3D 检测和分割 (三):有趣的可视化

    小伙伴们好呀,3D 检测和分割系列文章继续更新啦,在第一篇文章中我们带领大家了解了整个框架的大致流程,第二篇文章我们给大家解析了 MMDetection3D 中的坐标系和核心组件 Box,今天我们将带大家看看 3D 场景中的可视化组件 Visualizer,如何在多个模态数据上轻松可视化并且

    2023年04月21日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包