53. Protocol buffer 的Go使用

文章目录

  • 一、介绍
  • 二、安装
  • 三、protoc3语法
    • 1、 protoc3 与 protoc2区别
    • 2、proto3生成go代码
      • Message
      • 内嵌Message
      • 字段
        • 单一标量字段
        • 单一message字段
        • 可重复字段slice
        • map字段
        • 枚举

一、介绍

ProtobufGoogle旗下的一款平台无关,语言无关,可扩展的序列化结构数据格式。所以很适合用做数据存储和作为不同应用,不同语言之间相互通信的数据交换格式,只要实现相同的协议格式,即同一proto文件被编译成不同的语言版本,加入到各自的工程中去,这样不同语言就可以解析其他语言通过Protobuf序列化的数据。目前官网提供了C++,Python,JAVA,GO等语言的支持。

二、安装

  1. Mac上安装Protoc3
  2. Windows安装Protoc3:可以查看本人的另一篇博客:windows安装protoc、protoc-gen-go、protoc-gen-go-grpc
  3. 安装protoc-gen-go
    protoc-gen-go是生成Go代码的protocolbuffers编译器。可以理解为一个编译器插件,配合protoc来使用。在命令行执行如下命令即可完成安装:
go get -u github.com/golang/protobuf/protoc-gen-go@latest

网上资料推荐的基本都是这个命令,但目前该模块已被弃用,继续使用该命令将出现错误,提示该库已经被弃用,让我们使用go get -u google.golang.org/protobuf/
在这里插入图片描述

protoc-gen-go二进制文件,当编译器调用时传递了 --go_out命令行标志时, protoc就会使用它。--go_out告诉编译器把生成的Go源代码写到哪里。编译器会为每个 .proto文件生成一个单独的源代码文件。

输出文件的名称是通过获取.proto文件的名称并进行两处更改来计算的:

生成文件的扩展名是 .pb.go。比如说 player_record.proto编译后会得到 player_record.pb.go
使用 --proto_path-I命令行标志指定proto文件所在路径,使用 --go_out标志指定生成的go文件放在哪个路径下。
当你运行如下编译命令时:

protoc --proto_path=src --go_out=build/gen src/foo.proto src/bar/baz.proto

编译器会读取文件 src目录下的src/foo.protosrc/bar/baz.proto,这将会在build/gen目录下生成两个输出文件 build/gen/foo.pb.gobuild/gen/bar/baz.pb.go

如果有必要,编译器会自动生成 build/gen/bar目录,其中bar为我们在proto文件中指定的go文件包名,如option go_package = "/bar";,但是他不能创建 build或者 build/gen目录,这两个必须是已经存在的目录。

三、protoc3语法

1、 protoc3 与 protoc2区别

proto3proto2的基础上去掉了一些复杂的语法和特性,更强调约定而弱化语法。主要几点区别如下:

  1. proto文件第一行非空白非注释行,必须指定版本,syntax = "proto3";如果不指定,则默认是proto2
  2. 字段规则移除了 required,并把 optional改名为 singular,省略不写时,默认就是singular
  3. repeated字段默认采用 packed 编码,在 proto2 中,需要明确使用[packed=true] 来为字段指定比较紧凑的 packed 编码方式。
  4. 语言增加 Go、Ruby、JavaNano 支持,即在proto2时并不支持go语言。
  5. 移除了default 选项,在proto2中,可以使用default选项为某一字段指定默认值。在 proto3 中,字段的默认值只能根据字段类型由系统决定。也就是说,默认值全部是约定好的,而不再提供指定默认值的语法。在字段被设置为默认值的时候,该字段不会被序列化。这样可以节省空间,提高效率。
    但这样就无法区分某字段是根本没赋值,还是赋值了默认值。 所以一般需要避免将默认值作为任何行为的触发方式。例如
enum AudienceDisplayTypeEnum {
    NoValue = 0; // (占位符)说明端上没有传入此参数,请勿使用
    CurrentCount = 1; // 展示当前直播间内人数
    AccumulativeCount = 2; // 展示直播间累计人数
    SettingEntranceClosed = 99; // 端上拿到则不展示此选项,相当于配置项是否出现的开关
}

此例子是控制直播间展示在线人数还是看播人次的开关,开关仅两个取值:truefalse,但这里并没有使用bool类型,因为bool型默认值是false,即使前端没有给我们传该值,我们也会拿到false值,从而可能当成是前端传过来的值,切换开关,因此使用枚举。使用枚举后,不使用01表示开关的打开与关闭,因为0是枚举的默认值,也不应该作为控制行为的值,因此有业务含义的从序号为1的字段开始。
6. 枚举类型的第一个字段必须为 0,因为枚举会把第一个字段作为默认值
7. 增加了JSON映射特性,如

message XXXRequest {
  string  name = 1; 
  int64 begin_time = 2 (go.tag = "json:\"beginTime\"");
  int64 end_time = 3;

  int32 page_no = 4;
  int32 page_size = 5;
}

2、proto3生成go代码

Go Proto Buffer代码生成官网文档地址

如果一个.proto文件中有包声明,生成的源代码将会使用它来作为Go的包名,如果.proto的包名中有. ,在Go包名中会将.转换为_。举例来说proto包名example.high_score将会生成Go包名example_high_score

.proto文件中可以使用option go_package指令来覆盖上面默认生成Go包名的规则。比如说包含如下指令的一个.proto文件

package example.high_score;
option go_package = "/test";

生成的Go源代码的包名是test

如果一个.proto文件中不包含package声明,生成的源代码将会使用.proto文件的文件名作为Go包名,.会被首先转换为_。举例来说一个名为high.score.proto不包含package声明的文件将会生成文件high.score.pb.go,他的Go包名是high_score

Message

一个简单的message声明:

message Foo {}

protocol buffer编译器将会生成一个名为Foo的结构体,var A *Foo为实现了proto.Message接口的Foo类型的指针,因为Foo实现了proto.Message接口中ProtoMessage()方法,生成的XXX.pb.go文件将包含如下代码片段,注意看注释哦

type Foo struct {
}

// 重置proto为默认值
func (m *Foo) Reset()         { *m = Foo{} }

// String 返回proto的字符串表示
func (m *Foo) String() string { return proto.CompactTextString(m) }

// ProtoMessage作为一个tag 确保其他人不会意外的实现
// proto.Message 接口.
func (*Foo) ProtoMessage()    {}

内嵌Message

一个message可以声明在其他message的内部。比如:

message Foo {
  message Bar {
  }
}

这种情况,编译器会生成两个结构体:FooFoo_Bar

字段

编译器会为每个在message中定义的字段生成一个Go结构体的字段,字段的确切性质取决于它的类型以及它是singular,repeated,map还是oneof字段。

注意生成的Go结构体的字段将始终使用驼峰命名,即在.proto文件中消息字段用的是小写加下划线(工作中基本都是这种形式),生成的Go代码会是大驼峰命名。大小写转换的原理如下:

  • 首字母会大写,如果message中字段的第一个字符是_,它将被替换为X
  • 如果内部下划线后跟小写字母,则删除下划线,并将后面跟随的字母大写。
    因此,proto字段foo_bar_bazGo中变成FooBarBaz _my_field_name变为XMyFieldName
单一标量字段

对于包级别字段定义:

int32 id = 1;

编译器将生成一个带有名为Idint32字段和一个访问器方法GetId()的结构,该方法返回结构体中Id字段的零值(如果字段未设置(数值型零值为0,字符串为空字符串))。

单一message字段

给出如下消息类型

message Bar {}

对于一个有Bar类型字段的消息:

// proto3
message Baz {
  Bar foo = 1;
}

编译器将会生成一个Go结构体

type Baz struct {
  Foo *Bar
}

消息类型的字段可以设置为nil,这意味着该字段未设置。

编译器还生成一个func(m *Baz)GetFoo() *Bar辅助函数。这让不在中间检查nil值进行链式调用成为可能,因为该方法中会进行相关字段的nil判断。

可重复字段slice

每个重复的字段在Go中的结构中生成一个T类型的slice,其中T是字段的元素类型。对于带有重复字段的消息:

message Baz {
  repeated Bar foo = 1;
}

编译器会生成如下结构体:

type Baz struct {
   Foo  []*Bar
}

同样,对于字段定义repeated bytes foo = 1; 编译器将会生成一个带有类型为[][]byte, 名为Foo的字段的Go结构体。对于可重复的枚举repeated MyEnum bar = 2;,编译器会生成带有类型为[]MyEnum, 名为Bar的字段的Go结构体。

map字段

每个映射字段会在Go的结构体中生成一个map[TKey]TValue类型的字段,其中TKey是字段的键类型,TValue是字段的值类型。对于下面这个消息定义:

message Bar {}

message Baz {
  map<string, Bar> foo = 1;
}

编译器生成Go结构体

type Baz struct {
  Foo map[string]*Bar
}
枚举

给出如下枚举

message SearchRequest {
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 1;
}

编译器将会生成一个枚举类型和一系列该类型的常量。

对于消息中的枚举(像上面那样),类型名字以消息名开头

type SearchRequest_Corpus int32

对于包级别的枚举:

// .proto
enum Foo {
  DEFAULT_BAR = 0;
  BAR_BELLS = 1;
  BAR_B_CUE = 2;
}

Go中的类型不会对proto中的枚举名称进行修改:

type Foo int32

此类型具有String()方法,该方法返回给定值的名称。

Enum()方法使用给定值初始化新分配的内存并返回相应的指针:

func (Foo) Enum() *Foo

编译器为枚举中的每个值生成一个常量。对于消息中的枚举,常量以消息的名称开头:

const (
        SearchRequest_UNIVERSAL SearchRequest_Corpus = 0
        SearchRequest_WEB       SearchRequest_Corpus = 1
        SearchRequest_IMAGES    SearchRequest_Corpus = 2
        SearchRequest_LOCAL     SearchRequest_Corpus = 3
        SearchRequest_NEWS      SearchRequest_Corpus = 4
        SearchRequest_PRODUCTS  SearchRequest_Corpus = 5
        SearchRequest_VIDEO     SearchRequest_Corpus = 6
)

对于包级别的枚举,常量以枚举名称开头:

const (
        Foo_DEFAULT_BAR Foo = 0
        Foo_BAR_BELLS   Foo = 1
        Foo_BAR_B_CUE   Foo = 2
)

protobuf编译器还生成从整数值到字符串名称的映射以及从名称到值的映射:

var Foo_name = map[int32]string{
        0: "DEFAULT_BAR",
        1: "BAR_BELLS",
        2: "BAR_B_CUE",
}
var Foo_value = map[string]int32{
        "DEFAULT_BAR": 0,
        "BAR_BELLS":   1,
        "BAR_B_CUE":   2,
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/231771.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

time模块(python)

一.sleep休眠 [rootrhel8 day04]# vim demo01_time.py import time def banzhuan():print("搬砖")time.sleep(3.5) #让程序休眠3.5秒print("结束")banzhuan()[rootrhel8 day04]# python3 demo01_time.py 搬砖 结束运行时&#xff0c;会发现程序中间暂停…

征途漫漫:汽车MCU的国产替代往事

01.西雁东飞&#xff0c;南下创业 1985年&#xff0c;山东大学物理系毕业的周生明加入878厂&#xff08;“北霸天”&#xff09;参与MOS电路研发&#xff0c;随后几年&#xff0c;大洋彼岸的英特尔相继推出CPU 386\486、奔腾系列等产品。在摩尔定律的凸显、进口和走私的剧烈冲…

js/jQuery常见操作 之各种语法例子(包括jQuery中常见的与索引相关的选择器)

js/jQuery常见操作 之各种语法例子&#xff08;包括jQuery中常见的与索引相关的选择器&#xff09; 1. 操作table常见的1.1 动态给table添加title&#xff08;指定td&#xff09;1.1.1 给td动态添加title&#xff08;含&#xff1a;获取tr的第几个td&#xff09;1.1.2 动态加工…

RocketMQ-RocketMQ高性能核心原理(流程图)

1.NamesrvStartup 2.BrokerStartup 3. DefualtMQProducer 4.DefaultMQPushConsumer

Unity-Shader - 2DSprite描边效果

实现一个简单的2D精灵图描边效果&#xff0c;效果如下 实现思路&#xff1a; 可以通过判断该像素周围是否有透明度为 0的值&#xff0c;如果有&#xff0c;则说明该像素位于边缘。 所以我们需要打开alpha blend&#xff0c;即&#xff1a; Blend SrcAlpha OneMinusSrcAlpha&am…

Java 11 到 Java 21:无缝迁移的可视化指南

迁移到 Java 21 的理由 在我们探索从 Java 11 迁移到 Java 21 的必要性的旅程中&#xff0c;我们深入研究了四个关键类别&#xff0c;并强调了这一转变的重要性。每个方面都至关重要&#xff0c;共同为采用最新版本的 Java 编程语言打造了一个引人注目的案例。 1. 安全性&#…

【数据结构(九)】线索化二叉树(3)

文章目录 1. 前言——问题引出2. 线索二叉树的基本介绍3. 线索二叉树的应用案例3.1. 思路分析3.2. 代码实现 4. 遍历线索化二叉树4.1. 代码实现 1. 前言——问题引出 问题&#xff1a;     将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. &#xff08;n17个空指针域&…

【数据挖掘】国科大苏桂平老师数据库新技术课程作业 —— 第三次作业

part 1 设计一个学籍管理小系统。系统包含以下信息&#xff1a; 学号、学生姓名、性别、出生日、学生所在系名、学生所在系号、课程名、课程号、课程类型&#xff08;必修、选修、任选&#xff09;、学分、任课教师姓名、教师编号、教师职称、教师所属系名、系号、学生所选课…

前端:如何让background背景图片进行CSS自适应

在设置login背景时&#xff0c;找到了一张这样的图片&#xff1a; 但是设置成login背景时&#xff0c;如果没有做一些css适应设置&#xff0c;图片就变样了&#xff0c;变成了这样&#xff1a; 严重变形了&#xff0c;这就造成了一种理想与现实的差距。 若想解决这个自适应问题…

西工大网络空间安全学院计算机网络实验五——ACL配置

实验五、ACL配置 一. 实验目的 1. 掌握ACL的基本配置方法 二. 实验内容 1. 基于如下图所示的拓扑图&#xff0c;对路由器进行正确的RIP协议配置&#xff1b; ​ 首先引入3台2811 IOS15型号的路由器、3台2950-T24型号的交换机、4台PC-PT型号的PC机、两台Server-PT型号的服务…

Java:字节流 文件输出与读入方法 并 实现文件拷贝

文章目录 字节 流FileOutputStream换行 与 续写FileInputstream实现 文件拷贝&#xff08;字节数组 读入方法&#xff09;字节流 编码 字节 流 FileOutputStream 创建对象&#xff0c;指定位置&#xff08;产生数据传输通道&#xff09; 参数可以是File对象&#xff0c;也可以…

蓝桥杯日期问题

蓝桥杯其他真题点这里&#x1f448; 注意日期合法的判断 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main{static int[] days {0,31,28,31,30,31,30,31,31,30,31,30,31};static BufferedReader in new Buf…

功能强大的屏幕录制和剪辑工具Camtasia Studio 2024 中文版

Camtasia Studio 2024 是一款功能强大的屏幕录像工具&#xff0c;集视频录制、剪辑、编辑和播放于一体的多功能屏幕录制软件&#xff0c;Camtasia Studio 2024操作简单&#xff0c;它能够轻松为您将屏幕上的所有声音、影音、鼠标移动的轨迹和麦克风声音全部录制下来&#xff0c…

[足式机器人]Part2 Dr. CAN学习笔记-数学基础Ch0-9阈值选取-机器视觉中应用正态分布和6-sigma

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-数学基础Ch0-9阈值选取-机器视觉中应用正态分布和6-sigma 5M1E——造成产品质量波动的六因素 人 Man Manpower 机器 Machine 材料 Material 方法 Method 测量 Measurment 环境 Envrionment DMAI…

AlexNet

概念 过拟合:根本原因是特征维度过多&#xff0c;模型假设过于复杂&#xff0c;参数过多&#xff0c;训练数据过少&#xff0c;噪声过多&#xff0c;导致拟合的函数完美的预测训练集&#xff0c;但对新数据的测试集预测结果差。 过度的拟合了训练数据&#xff0c;而没有考虑到…

算法专题一:双指针

算法专题一&#xff1a;双指针 一&#xff1a;移动零1.GIF题目解析&#xff1a; 二&#xff1a;复写零2.GIF题目解析&#xff1a; 三&#xff1a;快乐数3.GIF题目解析&#xff1a; 四&#xff1a;装水最多容器&#xff1a;4.GIF题目解析&#xff1a; 五&#xff1a;有效三角形的…

0007Java程序设计-ssm基于微信小程序的在线考试系统

文章目录 **摘要**目 录系统实现开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;776871563 摘要 网络技术的快速发展给各行各业带来了很大的突破&#xff0c;也给各行各业提供了一种新的管理技术&#xff0c;基于微信小程序的在线考试…

Spring Boot中使用Swagger

1. 启用Swagger 1.1 启用注解扫描和文档接口 直接在POM文件引入依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version> </dependency>1.2 启动swagger-u…

系统设计-微服务架构

典型的微服务架构图 下图展示了一个典型的微服务架构。 负载均衡器&#xff1a;它将传入流量分配到多个后端服务。CDN&#xff08;内容交付网络&#xff09;&#xff1a;CDN 是一组地理上分布的服务器&#xff0c;用于保存静态内容以实现更快的交付。客户端首先在 CDN 中查找内…

ElementUI 时间选择器如何限定选择时间

DatePicker 日期选择器 | Element Plus 我们如何限定我们的选择时间呢&#xff0c;比如限定选择时间为今天之前&#xff0c;或者今天之后的时间&#xff1f; 我们可以使用官方提供的disabled-date来实现 我们通过这个属性 做一个回调函数&#xff0c;在里面比较我们想要限定的时…