微服务-ProtoBuf认识与使用

本文介绍了ProtocolBuffers(ProtoBuf)的概念、优势、安装步骤、与JSON和XML的对比,以及在Go语言中的应用,包括其在微服务中的角色、序列化/反序列化过程和高级用法,如嵌套消息、repeated字段和RPC服务定义。
摘要由CSDN通过智能技术生成

四. ProtoBuf认识与使用

4.1 protobuf简介

:::info
Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,可以用于结构化数据,或者说序列化。它很适合做 **数据存储 **或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。他是一
种灵活,高效,自动化的机制,用于序列化结构化数据,对比于 XML和JSON,他更小,更快、更简
单。总之他是微服务中需要使用的东西。
:::

:::info
Protobuf 刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成代码并实现将结构化数据序列化的功能。这里我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语 言RPC接口的基础工具。
:::

需要了解两点

  1. protobuf是类似与json一样的数据描述语言(数据格式)
  2. protobuf非常适合于RPC数据交换格式

接着我们来看一下protobuf的优势和劣势:
优势:
1:序列化后体积相比Json和XML很小,适合网络传输
2:支持跨平台多语言
3:消息格式升级和兼容性很好
4:序列化反序列化速度很快,快于Json的处理速度

**劣势: **
1:应用不够广(相比xml和json)
2:二进制格式导致可读性差
3:缺乏自描述
我们先安装一下protobuf,然后通过例子来看一下protobuf的具体作用。

4.2 protobuf的安装

4.2.1 windows电脑上面安装protocol buffers

1、安装 protobuf

:::tips
https://github.com/protocolbuffers/protobuf
:::

如下载protobuf v3.15.5 的版本

:::info
下载protobuf https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.5
这里我下载的 protoc-3.15.5-win64.zip
减压并将解压得到的文件中的bin目录路径添加到系统变量中
:::

查看版本

:::info
C:\Users\xxxx>protoc --version
libprotoc 3.15.2
:::

2、protobuf的go语言插件protoc-gen-go插件

go install github.com/golang/protobuf/protoc-gen-go@latest

4.2.2 Mac电脑上面安装protocol buffers

1、安装protobuf(方法一 推荐)
如果电脑上面没有brew先安装brew

brew install protobuf

2、安装protobuf(方法二)

如果电脑没有安装brew也可以使用下面方法安装

https://github.com/protocolbuffers/protobuf/releases

选择适合macos的protobuf,比如选择protoc-3.12.1-osx-x86_64.zip

解压包得到protoc-3.12.1-osx-x86_64

mv protoc-3.12.1-osx-x86_64 protobuf

配置环境变量

vim ~/.bash_profile
export PROTOBUF=/Users/nacos/Library/protobuf
export PATH=$PATH:$PROTOBUF/bin

配置完保存记得刷新下配置

source ~/.bash_profile

查看版本

protoc --version

**3、protobuf的go语言插件protoc-gen-go插件 **

go install github.com/golang/protobuf/protoc-gen-go@latest

安装完毕后如果提示没法使用protoc-gen-go,还需要把gopath对应的bin目录配置到环境变量
配置环境变量

vim ~/.bash_profile
export PATH=/Users/nacos/go/bin:$PATH

配置完保存记得刷新下配置

source ~/.bash_profile

4.2.3 测试protoc和protoc-gen-go是否全部配置成功

1、新建 test.proto 然后把下面代码复制过去

syntax = "proto3";

option go_package = "./protoService";

message Userinfo {
    string name = 1;
    int32 age = 2;
    repeated string hobby = 3;
    PhoneType phone=4;
}

2、使用下面命令把proto 文件生成go文件

protoc --go_out=./ *.proto
protoc --go_out=plugins=grpc:. *.proto

4.3 protobuf 简单语法

参考文档(需科学上网):https://developers.google.com/protocol-buffers/docs/proto3
首先让我们看一个非常简单的例子。

syntax = "proto3"; //指定版本信息,不指定会报错,默认是proto2
option go_package = "./proto;helloworld"; //分号前面的表示当前.proto文件所在的路径,
// 分号后面表示生成go文件的包名
// message定义一种消息类型,关键字message定义结构,并且结构中可以嵌套定义结构,message定义的内
// 容和生成一个结构体
message Person {
    // 名字
    string name = 1;
    // 年龄
    int32 age = 2 ;
    //爱好
    repeated string hobby = 3; // 数组 golang中会生成string类型的切片
}

protobuf 消息的定义(或者称为描述)通常都写在一个以 .proto 结尾的文件中。

该文件的第一行指定正在使用 proto3 语法:如果不这样做,协议缓冲区编译器将假定正在
使用proto2。这也必须是文件的第一个非空的非注释行。

第二行 option go_package 指定生成go文件的目录以及包名称

最后message关键字定义一个Person消息体,类似于go语言中的结构体,是包含一系列类
型数据的集合。许多标准的简单数据类型都可以作为字段类型,包括 bool , int32 ,
float , double ,和 string 。也可以使用其他message类型作为字段类型。

在message中有一个字符串类型的value成员,该成员编码时用1代替名字。我们知道,在
json中是通过成员的名字来绑定对应的数据,但是Protobuf编码却是通过成员的唯一编号来
绑定对应的数据,因此Protobuf编码后数据的体积会比较小,能够快速传输,缺点是不利于
阅读。

message的格式说明
消息由至少一个字段组合而成,类似于Go语言中的结构体,每个字段都有一定的格式:

//注释格式 注释尽量也写在内容上方
(字段修饰符)数据类型 字段名称 = 唯一的编号标签值;

唯一的编号标签:代表每个字段的一个唯一的编号标签,在同一个消息里不可以重复。这些
编号标签用与在消息二进制格式中标识你的字段,并且消息一旦定义就不能更改。需要说明
的是标签在1到15范围的采用一个字节进行编码,所以通常将标签1到15用于频繁发生的消
息字段。编号标签大小的范围是1到2的29次。19000-19999是官方预留的值,不能使用。

注释格式:向.proto文件添加注释,可以使用C/C++/java/Go风格的双斜杠(//) 语法格式
或者 //

message常见的数据类型与go中类型对比

image.png

4.4 protobuf高级用法

protobuf除了上面的简单类型还有一些复杂的用法,如下:
message嵌套
messsage除了能放简单数据类型外,还能存放另外的message类型,如下:

syntax = "proto3"; //指定版本信息,不指定会报错
option go_package = "./proto;helloworld";
//message为关键字,作用为定义一种消息类型
message Person {
    // 名字
    string name = 1;
    // 年龄
    int32 age = 2 ;
    //定义一个message
    message PhoneNumber {
        string number = 1;
        int64 type = 2;
    }
    PhoneNumber phone = 3;
}

repeated关键字
repeadted关键字类似与go中的切片,编译之后对应的也是go的切片,用法如下:

syntax = "proto3"; //指定版本信息,不指定会报错
option go_package = "./proto;helloworld";
//message为关键字,作用为定义一种消息类型
message Person {
    // 名字
    string name = 1;
    // 年龄
    int32 age = 2 ;
    
    // 定义一个message
    message PhoneNumber {
        string number = 1;
        int64 type = 2;
    }
    repeated PhoneNumber phone = 3;
}

默认值
:::info
解析数据时,如果编码的消息不包含特定的单数元素,则解析对象对象中的相应字段将设置为该字段的
默认值。不同类型的默认值不同,具体如下:
对于字符串,默认值为空字符串。
对于字节,默认值为空字节。
对于bools,默认值为false。
对于数字类型,默认值为零。
对于枚举,默认值是第一个定义的枚举值,该必须为0。
repeated字段默认值是空列表
message字段的默认值为空对象
:::

**enum关键字 **
在定义消息类型时,可能会希望其中一个字段有一个预定义的值列表。比如说,电话号码字段有个类
型,这个类型可以是,home,work,mobile。我们可以通过enum在消息定义中添加每个可能值的常量
来非常简单的执行此操作。实例如下:

syntax = "proto3"; //指定版本信息,不指定会报错
package pb; //后期生成go文件的包名
//message为关键字,作用为定义一种消息类型
message Person {
    // 名字
    string name = 1;
    // 年龄
    int32 age = 2 ;
    //定义一个message
    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
    }
    repeated PhoneNumber phone = 3;
}


//enum为关键字,作用为定义一种枚举类型
enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}

如上,enum的第一个常量映射为0,每个枚举定义必须包含一个映射到零的常量作为其第一个元
素。这是因为:
必须有一个零值,以便我们可以使用0作为数字默认值。
零值必须是第一个元素,以便与proto2语义兼容,其中第一个枚举值始终是默认值。

定义RPC服务

如果需要将message与RPC一起使用,则可以在 .proto 文件中定义RPC服务接口,protobuf编译器将
根据你选择的语言生成RPC接口代码。示例如下:

//定义RPC服务
service HelloService {
	rpc Hello (Person)returns (Person);
}

定义一个RPC的服务

syntax = "proto3";
option go_package = "./service";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
    string name = 1;
}
message HelloReply {
    string message = 1;
}
//protoc --go_out=plugins=grpc:. *.proto

4.5 protobuf基本编译

protobuf编译是通过编译器protoc进行的,通过这个编译器,我们可以把.proto文件生成
go,Java,Python,C++, Ruby, JavaNano, Objective-C,或者C# 代码,生成命令如下:

protoc --proto_path=IMPORT_PATH --go_out=DST_DIR path/to/file.proto
  1. –proto_path=IMPORT_PATH,IMPORT_PATH是 .proto 文件所在的路径,如果忽略则默认
    当前目录。如果有多个目录则可以多次调用–proto_path,它们将会顺序的被访问并执行导
    入。
  2. –go_out=DST_DIR, 指定了生成的go语言代码文件放入的文件夹
  3. 允许使用 protoc --go_out=./ *.proto 的方式一次性编译多个 .proto 文件
  4. go语言编译时,protobuf 编译器会把 .proto 文件编译成 .pd.go 文件

一般在使用的时候我们都是使用下面这种简单的命令:

protoc --go_out=./ *.proto

然后我们给这个 .proto 文件中添加一个RPC服务,再次进行编译,发现生成的go文件没有发生变化。
这是因为世界上的RPC实现有很多种,protoc编译器并不知道该如何为HelloService服务生成代码。不
过在protoc-gen-go内部已经集成了一个叫grpc的插件,可以针对grpc生成代码:

protoc --go_out=plugins=grpc:. *.proto

和我们优化之后的RPC服务有点相似,但是又不太一样,接下来就让我们来学习一下grpc框架。

4.6 protobuf 序列化反序列化

新建proto/test.proto

syntax = "proto3";
option go_package = "./protoService";
message Userinfo {
    string name = 1;
    int32 age = 2;
    repeated string hobby = 3;
    PhoneType phone=4;
}
//enum为关键字,作用为定义一种枚举类型
enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}
protoc --go_out=./ *.proto
protoc --go_out=plugins=grpc:. *.proto

main.go中实现序列化和反序列化

package main
import (
    "fmt"
    "protobufers/proto/protoService"
    "google.golang.org/protobuf/proto"
)

func main() {
    
    u := &protoService.Userinfo{
        Username: "zhangsan",
        Age: 20,
        Hobby: []string{"吃饭", "睡觉", "写代码"},
    }
    
    fmt.Println(u.GetHobby())
    // proto.Marshald对protoBufer进行序列化
    data, err1 := proto.Marshal(u)
    if err1 != nil {
        fmt.Println(err1)
    }
    fmt.Println(data)
    //proto.Unmarshal可以对protoBufer进行反序列化
    info := protoService.Userinfo{}
    err2 := proto.Unmarshal(data, &info)
    if err2 != nil {
        fmt.Println(err2)
    }
    fmt.Printf("%#v", info)
    fmt.Println(info.GetHobby())
    // protoc --go_out=./ *.proto
}
  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用以下命令安装 google-protobuf: npm install --save google-protobuf 这个命令会将 google-protobuf 模块安装到你的项目,并将其保存为依赖项。 然后,你可以使用以下路径引入 empty_pb.js 模块: google-protobuf/google/protobuf/empty_pb.js 这将允许你在代码使用 empty_pb.js 模块。请确保在你的代码正确引入这个路径。 需要注意的是,确保你已经在安装 google-protobuf 之前完成了 protobufjs 的安装,以便确保这两个模块之间的依赖关系正确。如果你遇到了安装问题或错误提示,你可以使用 --force 选项来强制执行安装命令,以解决已存在的文件问题。 希望这些信息对你有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [protobuf.js的使用](https://blog.csdn.net/qq_27868061/article/details/114038738)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [npm install -g protobufjs](https://blog.csdn.net/qifenzhisanjin/article/details/124952503)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值