不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Langchain
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Langchain
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • GO基础

  • 面向对象

  • 并发编程

  • 常用库

  • 数据库操作

  • Beego框架

  • Beego商城

  • GIN框架

  • GIN论坛

  • 微服务

    • 01.RPC
    • 02.protobuf
      • 01.protobuf
        • 1、What 是什么
        • 2、Why 为什么使用
        • 3、When 什么时候使用
        • 4、原理解析
        • 1)字段编号
        • 2)二进制序列化
        • 3)消息灵活性
        • 5、紧凑的二进制格式
      • 02.protobuf安装
        • 1、protobuf说明
        • 2、安装protobuf
      • 03.protobuf生成go文件格式
        • 1、demo/pb/myproto.proto
        • 2、myproto.pb.go
      • 04.protobuf转json
        • 1、db/class.proto
        • 2、db/class/class.pb.go
        • 3、test-proto.go
    • 03.gRPC
    • 04.micro简介
    • 05.consul
    • 06.go-micro
    • 07.nacos安装
    • 08.grpc注册nacos
    • 09.gin_grpc_nacos
    • 10.限流
  • 设计模式

  • Go
  • 微服务
xiaonaiqiang
2021-11-08
目录

02.protobuf

# 01.protobuf

# 1、What 是什么

  • Protocol Buffers(Protobuf) 是由 Google 开发的一种语言中立、平台无关、可扩展的序列化协议,用于高效地结构化数据交换
  • 在 Golang 中,Protobuf 常用于与 gRPC 结合,通过定义数据结构来实现跨语言的数据序列化与反序列化
  • Protobuf 文件通常以 .proto 为后缀,通过编译器生成相应的 Golang 代码,简化了数据传输的过程

# 2、Why 为什么使用

  • 高效传输:
    • Protobuf 使用二进制格式,数据传输效率远高于基于文本格式的 JSON 或 XML
    • 它序列化后的数据占用空间小,反序列化速度快,非常适合性能要求高的系统
  • 跨语言支持:
    • Protobuf 支持多种编程语言(如 Golang、Java、Python 等)
    • 允许不同语言的系统通过统一的格式进行通信
  • 结构化数据:
    • Protobuf 能够明确定义消息的数据结构
    • 通过 .proto 文件维护字段类型和顺序,从而确保数据一致性
  • 向后兼容性:
    • Protobuf 的字段编号机制允许向后兼容,支持在不破坏旧系统的情况下添加新字段

# 3、When 什么时候使用

  • 大规模分布式系统:当系统需要在不同微服务之间传输大量数据时,Protobuf 提供了高效的序列化和反序列化方式
  • 需要跨语言通信:如果不同的服务由不同编程语言开发,Protobuf 可以确保数据格式一致,减少数据解析的复杂性
  • 高性能要求:在实时性要求高的场景中(如流媒体、实时数据处理),Protobuf 的轻量级数据结构和高效传输速度尤为重要
  • gRPC 场景:Protobuf 是 gRPC 默认使用的序列化协议,适用于各种远程过程调用(RPC)场景

# 4、原理解析

# 1)字段编号

  • 每个字段都有唯一的编号,数据在序列化时按照编号进行排列
  • 编号越小,序列化后的字节越短,这使得 Protobuf 在网络传输中更高效
  • 示例:假设你定义了一个包含用户信息的 Protobuf 消息
    • id = 1 对应的编号 1 只需占用 1 个字节,而如果编号为 128,则需要使用 2 个字节
    • 即使消息内容相同,SmallNumbers 的序列化结果会比 LargeNumbers 更短,这在大量数据传输时能显著节省带宽和提升性能
message SmallNumbers {
    string name = 1;
    int32 age = 2;
}
message LargeNumbers {
    string name = 128;
    int32 age = 129;
}
1
2
3
4
5
6
7
8

# 2)二进制序列化

  • Protobuf 将消息序列化为紧凑的二进制格式,减少了数据传输所需的字节数
  • 这与 JSON 或 XML 的文本格式不同,极大减少了带宽消耗和 CPU 处理时间
  • 示例:比较 Protobuf 和 JSON 格式在序列化后的结果
// 假设有以下 Protobuf 消息
message User {
    int32 id = 1;
    string name = 2;
    bool is_active = 3;
}

// JSON 序列化需要传输的字节数为(总共约 46 字节)
{"id": 123, "name": "Alice", "is_active": true}

// Protobuf 二进制序列化,总共约 11 字节  
08 7B 12 05 41 6C 69 63 65 18 01
1
2
3
4
5
6
7
8
9
10
11
12
  • 二进制序列化解析为json推演
# 08 7B   =>  "id": 123
`08`  字段编号为 1,数据类型为 Varint(整数)
`7B`  的十进制是 123

`12`  字段编号为 2,数据类型为 Length-delimited(字符串、字节数组)
`05`  表示接下来字符串的长度为 5 个字节
`41 6C 69 63 65`  这是字符串 "Alice" 的 ASCII 编码
1
2
3
4
5
6
7

# 3)消息灵活性

  • protobuf中只会新增不会删除字段,加入新增了 email 字段
  • 旧版服务会忽略 email 字段的额外数据,但仍然能够正确解析 id 和 name 字段
message User {
    int32 id = 1;
    string name = 2;
    string email = 3; // 新增字段
}
1
2
3
4
5

# 5、紧凑的二进制格式

  • 紧凑的二进制格式指的是一种高效的数据编码方式,它使用尽量少的位数来表示数据,以减少数据存储和传输的字节数
  • 在这种格式中,每一位或字节都经过压缩编码,最大化利用有限的存储空间
  • 1)字段编号代替字段名

  • 2)可变长度编码(Varint)

    • 小的数字(例如 127)只使用 1 个字节。
    • 大的数字(例如 128 或更大)则使用多个字节来表示。
  • 3)类型和长度信息整合

    • 字段编号通过一个“字段编号 * 8 + 类型”的规则进行编码。
    • 例如,编号 1 且类型为 Varint 的字段会被编码为 08,其中 1 * 8 + 0 = 8,表示 id 是一个整数类型。
  • 4)长度前缀的使用

    • 字符串 "Alice" 会被编码为 05 41 6C 69 63 65,其中 05 表示后续 5 个字节是字符串内容

# 02.protobuf安装

# 1、protobuf说明

  • 为了实现跨语言调用,在golang中实现RPC方法的时候我们应该选择一种跨语言的数据编解码方式,比如JSON

  • 上述的jsonrpc可以满足此要求,但是也存在一些缺点,比如不支持http传输,数据编解码性能不高等

  • 于是呢,一些第三方rpc库都选择采用protobuf进行数据编解码,并提供一些服务注册代码自动生成功能

  • Protobuf中最基本的数据单元是message,是类似Go语言中结构体的存在

    • 在message中可以嵌套message或其它的基础数据类型的成员

# 2、安装protobuf

root@dev:opt#  cd /opt
root@dev:opt#  apt-get install libffi6=3.2.1-4 -y
root@dev:opt#  sudo apt-get install autoconf automake libtool curl make g++ unzip libffi-dev -y

root@dev:opt#  git clone https://github.com/protocolbuffers/protobuf.git
root@dev:protobuf#  ./autogen.sh 
root@dev:protobuf#  ./configure
root@dev:protobuf#  make && make install
root@dev:protobuf#  sudo ldconfig

root@dev:protobuf#  protoc -h 
1
2
3
4
5
6
7
8
9
10
11
  • 安装protobuf的go语言插件
# 1) 下载
root@dev:opt#  cd /opt
root@dev:opt#  export GOPROXY=https://goproxy.cn
root@dev:opt#  go get -v -u github.com/golang/protobuf/proto
# 2) 进入到文件夹内进行编译
root@dev:opt#  find / -name protoc-gen-go
root@dev:opt#  cd /home/GOPATH/pkg/mod/github.com/golang/protobuf@v1.5.2/protoc-gen-go
root@dev:opt#  go build
# 3)将生成的protoc-gen-go可执行文件,放在/bin目录下
root@dev:protoc-gen-go# cp protoc-gen-go /bin/
# 4)尝试补齐 protoc-gen-go 
1
2
3
4
5
6
7
8
9
10
11

# 03.protobuf生成go文件格式

# 1、demo/pb/myproto.proto

// 采用proto3的语法(默认是 proto2)
syntax = "proto3";

option go_package = "./student";

// 指定所在包包名
package pb;


// 定义枚举类型
enum Week {
  Monday = 0;   // 枚举值,必须从 0 开始.
  Turesday = 1;
}

// 定义消息体
message Student {
  int32 age = 1;  // 可以不从1开始, 但是不能重复. -- 不能使用 19000 - 19999
  string name = 2;
  People p = 3;
  repeated int32 score = 4;  // 数组
  // 枚举
  Week w = 5;
  // 联合体
  oneof data {
    string teacher = 6;
    string class = 7;
  }
}

// 消息体可以嵌套
message People {
  int32 weight = 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 2、myproto.pb.go

  • 转成go语言格式
root@dev:db#  protoc --go_out=./  *proto       # 将所有.proto结尾的文件全部进行编译
root@dev:db#  protoc --go_out=.  myproto.proto      # 指定对 myproto.proto进行编译
1
2

# 04.protobuf转json

# 1、db/class.proto

  • 转成go语言格式

    •    root@dev:db#  protoc --go_out=. class.proto 
      
      1
// 默认是 proto2
syntax = "proto3";

option go_package = "./class";

// 指定所在包包名
package pb;

// 班级:定义消息体
message Class {
  // 可以不从1开始, 但是不能重复. -- 不能使用 19000 - 19999
  string class_name = 1;
  Student stu = 2;
}

// 学生:消息体可以嵌套
message Student {
  string name = 1;
  int32 age = 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2、db/class/class.pb.go

// 指定所在包包名
package class

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

// 班级:定义消息体
type Class struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	// 可以不从1开始, 但是不能重复. -- 不能使用 19000 - 19999
	ClassName string   `protobuf:"bytes,1,opt,name=class_name,json=className,proto3" json:"class_name,omitempty"`
	Stu       *Student `protobuf:"bytes,2,opt,name=stu,proto3" json:"stu,omitempty"`
}


// 学生:消息体可以嵌套
type Student struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age  int32  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}

func (x *Student) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *Student) GetAge() int32 {
	if x != nil {
		return x.Age
	}
	return 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 3、test-proto.go

package main

import (
	"demo/db/class"
	"encoding/json"
	"fmt"
)

func main()  {
	cls := &class.Class{
		ClassName: "golang学神班级",
		Stu: &class.Student{
			Name: "zhangsan",
			Age: 23,
		},
	}
	var s, _ = json.Marshal(cls)
	jsonStr := string(s)
	fmt.Println(jsonStr)   
	// {"class_name":"golang学神班级","stu":{"name":"zhangsan","age":23}}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上次更新: 2024/10/15 16:27:13
01.RPC
03.gRPC

← 01.RPC 03.gRPC→

最近更新
01
300.整体设计
06-10
02
06.LangGraph
06-09
03
202.AI销售智能体
06-07
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式