自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

【Go微服務(wù)】一文帶你玩轉(zhuǎn)ProtoBuf

開(kāi)發(fā) 前端
通過(guò)這篇文章,我們不僅學(xué)會(huì)了ProtoBuf的入門(mén)操作,還使用Go語(yǔ)言基于ProtoBuf編碼解碼了數(shù)據(jù),進(jìn)行了實(shí)戰(zhàn)。進(jìn)階部分帶大家了解了ProtoBuf如何定義消息、ProtoBuf和Go數(shù)據(jù)類型的映射、枚舉類型如何使用、通過(guò)消息嵌套復(fù)用代碼、使用map類型時(shí)需要注意的問(wèn)題和小技巧。

前言

在網(wǎng)絡(luò)通信和通用數(shù)據(jù)交換等應(yīng)用場(chǎng)景中經(jīng)常使用的技術(shù)是 JSON 或 XML,在微服務(wù)架構(gòu)中通常使用另外一個(gè)數(shù)據(jù)交換的協(xié)議的工具ProtoBuf。

ProtoBuf也是我們做微服務(wù)開(kāi)發(fā),進(jìn)行Go進(jìn)階實(shí)戰(zhàn)中,必知必會(huì)的知道點(diǎn)。

今天就開(kāi)始第一章內(nèi)容:《一文帶你玩轉(zhuǎn)ProtoBuf》

5分鐘入門(mén)

1.1 簡(jiǎn)介

你可能不知道ProtoBuf,但一定知道json或者xml,從一定意義上來(lái)說(shuō)他們的作用是一樣的。

ProtoBuf全稱:protocol buffers,直譯過(guò)來(lái)是:“協(xié)議緩沖區(qū)”,是一種與語(yǔ)言無(wú)關(guān)、與平臺(tái)無(wú)關(guān)的可擴(kuò)展機(jī)制,用于序列化結(jié)構(gòu)化數(shù)據(jù)。

和json\xml最大的區(qū)別是:json\xml都是基于文本格式,ProtoBuf是二進(jìn)制格式。

ProtoBuf相比于json\XML,更小(3 ~ 10倍)、更快(20 ~ 100倍)、更為簡(jiǎn)單。

我們只需要定義一次數(shù)據(jù)結(jié)構(gòu),就可以使用ProtoBuf生成源代碼,輕松搞定在各種數(shù)據(jù)流和各種語(yǔ)言中寫(xiě)入、讀取結(jié)構(gòu)化數(shù)據(jù)。

1.2 安裝

建議大家使用主流版本v3,這是官網(wǎng)下載地址:https://github.com/protocolbuffers/ProtoBuf/releases

注意,不同的電腦系統(tǒng)安裝包是不一樣的:

  • Windows 64位 點(diǎn)這里下載
  • Windows 32位 點(diǎn)這里下載
  • Mac Intel 64位 點(diǎn)這里下載
  • Mac ARM 64位 點(diǎn)這里下載
  • Linux 64位 點(diǎn)這里下載

(公眾號(hào)無(wú)法跳轉(zhuǎn)到外鏈,點(diǎn)擊文末的閱讀原文可以跳轉(zhuǎn)到下載地址。)

小技巧:Mac查看自己的芯片類型點(diǎn)擊左上角的蘋(píng)果圖標(biāo),再點(diǎn)擊關(guān)于本機(jī),就可以查看了。

比如,我的處理器芯片是intel的,下載安裝包之后是這樣的:

bin目錄下的protoc是ProtoBuf的工具集,下文會(huì)重點(diǎn)介紹它的使用。

注意:我們需要將下載得到的可執(zhí)行文件protoc所在的 bin 目錄加到我們電腦的環(huán)境變量中。

Mac安裝小技巧

如果你的Mac安裝了brew,安裝ProtoBuf就更簡(jiǎn)單了,我們使用brew install ProtoBuf就可以了

1.3 編譯go語(yǔ)言的工具包

這個(gè)protoc可以將proto文件編譯為任何語(yǔ)言的文件,想要編譯為go語(yǔ)言的,還需要下載另外一個(gè)可執(zhí)行文件

命令是這樣的:

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

1.4 編寫(xiě)proto代碼

下面就編寫(xiě)一個(gè)非常簡(jiǎn)單,但是五臟齊全的proto代碼,我們?cè)俑鶕?jù)這段代碼生成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代碼,非常簡(jiǎn)單,使用下面的命令就可以了。

切換到.proto文件所在目錄

cd proto/demo/

指定proto源文件,自動(dòng)生成代碼。

protoc --go_out=. hello.proto

執(zhí)行上面的命令后,我們?cè)陧?xiàng)目中就自動(dòng)生成了一個(gè).pb.go的文件

入門(mén)ProtoBuf就是這么的簡(jiǎn)單:通過(guò)這幾步我們就完成了ProtoBuf的下載、安裝、編寫(xiě)了一個(gè)proto文件,并生成了能用Go語(yǔ)言讀寫(xiě)ProtoBuf的源代碼。

我們?cè)偕钊肓私庖幌聀robuf的用法:

10分鐘進(jìn)階

下面再帶大家深入了解一下ProtoBuf的知識(shí)點(diǎn),避免在開(kāi)發(fā)中踩坑。

小技巧:寫(xiě)proto和寫(xiě)go最大的區(qū)別是需要在結(jié)尾添加分號(hào)的;,在開(kāi)發(fā)過(guò)程中給自己提個(gè)醒:如果是寫(xiě)proto需要加分號(hào),如果是寫(xiě)go不需要加分號(hào)。

以我們上面的proto入門(mén)代碼舉例:

1.1 關(guān)鍵字

  • syntax:是必須寫(xiě)的,而且要定義在第一行;目前proto3是主流,不寫(xiě)默認(rèn)使用proto2
  • package:定義我們proto文件的包名
  • option go_package:定義生成的pb.go的包名,我們通常在proto文件中定義。如果不在proto文件中定義,也可以在使用protoc生成代碼時(shí)指定pb.go文件的包名

message:非常重要,用于定義消息結(jié)構(gòu)體,不用著急,下文會(huì)重點(diǎn)講解

細(xì)心的小伙伴一定注意到了 message 消息體中有一個(gè) “repeated” 關(guān)鍵字,這在我們寫(xiě)Go的時(shí)候是沒(méi)有的。

這是干什么用的呢?下面來(lái)詳細(xì)解答一下:

1.2 數(shù)組類型

關(guān)于數(shù)組類型,和Java、Go、PHP等語(yǔ)言中,定義數(shù)據(jù)類型不一樣。

在ProtoBuf消息中定義數(shù)組類型,是通過(guò)在字段前面增加repeated關(guān)鍵詞實(shí)現(xiàn),標(biāo)記當(dāng)前字段是一個(gè)數(shù)組。

只要使用repeated標(biāo)記類型定義,就表示數(shù)組類型。

我們來(lái)舉兩個(gè)例子:

(1)整數(shù)數(shù)組:

下面定義的arrays表示int32類型的數(shù)組

message Msg {
repeated int32 arrays = 1;
}

(2)字符串?dāng)?shù)組

下面定義的names表示字符串?dāng)?shù)組

message Msg {
repeated string names = 1;
}

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

1.3 消息

消息(message),在ProtoBuf中指的就是我們要定義的數(shù)據(jù)結(jié)構(gòu)。類似于Go中定義結(jié)構(gòu)體。

message關(guān)鍵詞用法也非常簡(jiǎn)單:

(1) 語(yǔ)法

syntax = "proto3";

message 消息名 {
消息體
}

例子:

syntax = "proto3";

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

定義了一個(gè)Request消息,這個(gè)消息有3個(gè)字段,query是字符串類型,page和limit是int32類型。

1.4 字段類型

ProtoBuf支持多種數(shù)據(jù)類型,例如:string、int32、double、float等等,我整理了一份ProtoBuf和go語(yǔ)言的數(shù)據(jù)類型映射表

.proto Type

Go Type

使用技巧

double

float64

沒(méi)特殊技巧,記住float對(duì)應(yīng)go的float32,double對(duì)應(yīng)go的float64就可以了

float

float32

沒(méi)特殊技巧,記住float對(duì)應(yīng)go的float32,double對(duì)應(yīng)go的float64就可以了

int32

int32

使用變長(zhǎng)編碼,對(duì)于負(fù)值的效率很低,如果你的域有可能有負(fù)值,請(qǐng)使用sint64替代

uint32

uint32

使用變長(zhǎng)編碼

uint64

uint64

使用變長(zhǎng)編碼

sint32

int32

使用變長(zhǎng)編碼,這些編碼在負(fù)值時(shí)比int32高效的多

sint64

int64

使用變長(zhǎng)編碼,有符號(hào)的整型值。編碼時(shí)比通常的int64高效。

fixed32

uint32

總是4個(gè)字節(jié),如果數(shù)值都比228大的話,這個(gè)類型會(huì)比uint32高效。

fixed64

uint64

總是8個(gè)字節(jié),如果數(shù)值都比256大的話,這個(gè)類型會(huì)比uint64高效。

sfixed32

int32

總是4個(gè)字節(jié)

sfixed64

int64

總是8個(gè)字節(jié)

bool

bool

嚴(yán)格對(duì)應(yīng),玩不出其他花樣來(lái)

string

string

一個(gè)字符串必須是UTF-8編碼或者7-bit ASCII編碼的文本。

bytes

[]byte

可以包含任意順序的字節(jié)數(shù)組

1.5 分配標(biāo)識(shí)號(hào)

細(xì)心的小伙伴可能又有疑問(wèn)了,上面消息體中的 string query = 1; 這個(gè)1是什么呢?

這些數(shù)字是“分配表示號(hào)”:在消息定義中,每個(gè)字段后面都有一個(gè)唯一的數(shù)字,這個(gè)就是標(biāo)識(shí)號(hào)。

這些標(biāo)識(shí)號(hào)的作用是:用來(lái)在消息的二進(jìn)制格式中識(shí)別各個(gè)字段的,一旦開(kāi)始使用就不能夠再改變。

注意:分配標(biāo)識(shí)號(hào)在每個(gè)消息內(nèi)唯一,不同的消息體是可以擁有相同的標(biāo)識(shí)號(hào)的。

小技巧:[1,15]之內(nèi)的標(biāo)識(shí)號(hào)在編碼的時(shí)候會(huì)占用一個(gè)字節(jié)。[16,2047]之內(nèi)的標(biāo)識(shí)號(hào)則占用2個(gè)字節(jié)。所以應(yīng)該為那些頻繁出現(xiàn)的消息元素保留 [1,15]之內(nèi)的標(biāo)識(shí)號(hào)。

1.5.1 保留標(biāo)識(shí)號(hào)(Reserved)

小技巧:要為將來(lái)有可能添加的、頻繁出現(xiàn)的字段預(yù)留一些標(biāo)識(shí)號(hào)。

我們想保留一些標(biāo)識(shí)號(hào),留給以后用,可以使用下面語(yǔ)法:

message Test {
reserved 2, 5, 7 to 10; // 保留2,5,7到10這些標(biāo)識(shí)號(hào)
}

如果使用了這些保留的標(biāo)識(shí)號(hào),protocol buffer編譯器無(wú)法編譯通過(guò),將會(huì)輸出警告信息。

1.6 將消息編譯成各種語(yǔ)言版本的類庫(kù)

編譯器命令格式:

protoc [OPTION] PROTO_FILES

OPTION是命令的選項(xiàng), PROTO_FILES是我們要編譯的proto消息定義文件,支持多個(gè)。

常用的OPTION選項(xiàng):

--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 代碼

因?yàn)殚_(kāi)篇我們就用Go舉了例子,下面再用Java舉個(gè)例子吧:

protoc --java_out=. hello.proto

在當(dāng)前目錄導(dǎo)出java版本的代碼,編譯hello.proto消息,執(zhí)行效果如下:

下載再帶小伙伴們了解一下ProtoBuf的進(jìn)階知識(shí)點(diǎn)吧:枚舉類型、消息嵌套和Map類型。

1.7 枚舉類型

寫(xiě)Java的同學(xué)枚舉一定用的很溜,但是寫(xiě)Go的同學(xué)可能有點(diǎn)懵了,Go是不直接支持枚舉的,并沒(méi)有Enum關(guān)鍵字。

關(guān)注我,后續(xù)會(huì)詳解Go枚舉相關(guān)的知識(shí)點(diǎn),在這篇文章中不做重點(diǎn)介紹。

使用枚舉的場(chǎng)景是這樣的:

當(dāng)定義一個(gè)消息類型的時(shí)候,可能想為一個(gè)字段指定“預(yù)定義值”中的其中一個(gè)值,這時(shí)候我們就可以通過(guò)枚舉實(shí)現(xiàn),比如這種:

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

enum SexType //枚舉消息類型,使用enum關(guān)鍵詞定義,一個(gè)性別類型的枚舉類型
{
UNKONW = 0; //proto3版本中,首成員必須為0,成員不應(yīng)有相同的值
MALE = 1; //1
FEMALE = 2; //20未知
}

// 定義一個(gè)用戶消息
message UserInfo
{
string name = 1; // 姓名字段
SexType sex = 2; // 性別字段,使用SexType枚舉類型
}

運(yùn)行效果如下:

在實(shí)際開(kāi)發(fā)中,我們需要定義很多的proto,我們?nèi)绾巫龅较⒌膹?fù)用呢?

答案就是:“消息嵌套”

1.8 消息嵌套

我們?cè)陂_(kāi)發(fā)Java和PHP時(shí),經(jīng)常嵌套使用類,也可以使用其他類作為自己的成員屬性類型;在開(kāi)發(fā)Go時(shí)經(jīng)常嵌套使用結(jié)構(gòu)體。

在ProtoBuf中同樣支持消息嵌套,可以在一個(gè)消息中嵌套另外一個(gè)消息,字段類型可以是另外一個(gè)消息類型。

我們來(lái)看下面3個(gè)經(jīng)典示例:

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

// 定義Article消息
message Article {
string url = 1;
string title = 2;
repeated string tags = 3; // 字符串?dāng)?shù)組類型
}

// 定義ListArticle消息
message ListArticle {
// 引用上面定義的Article消息類型,作為results字段的類型
repeated Article articles = 1; // repeated關(guān)鍵詞標(biāo)記,說(shuō)明articles字段是一個(gè)數(shù)組
}

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導(dǎo)入其他proto文件定義的消息

我們?cè)趯?shí)際開(kāi)發(fā)中,通常要定義很多消息,如果都寫(xiě)在一個(gè)proto文件,是不方便維護(hù)的。

小技巧:將消息定義寫(xiě)在不同的proto文件中,在需要的時(shí)候可以通過(guò)import導(dǎo)入其他proto文件定義的消息。

例子:

創(chuàng)建文件: article.proto

syntax = "proto3";

package nesting;

option go_package = "./;article";

message Article {
string url = 1;
string title = 2;
repeated string tags = 3; // 字符串?dāng)?shù)組類型
}

創(chuàng)建文件: list_article.proto

syntax = "proto3";
// 導(dǎo)入Article消息定義
import "article.proto";

package nesting;

option go_package = "./;article";

// 定義ListArticle消息
message ListArticle {
// 使用導(dǎo)入的Result消息
repeated Article articles = 1;
}

執(zhí)行效果如下,我們順利生成了.pb.go文件:

1.9 map類型

我們?cè)贕o語(yǔ)言開(kāi)發(fā)中,最常用的就是切片類型和map類型了。

切片類型在ProtoBuf中對(duì)應(yīng)的就是repeated類型,前面我們已經(jīng)介紹過(guò)了。

再重點(diǎn)介紹一下map類型,ProtoBuf也是支持map類型的:

1.9.1 map語(yǔ)法

map<key_type, value_type> map_field = N;

語(yǔ)法非常簡(jiǎn)單和通用,但是有幾個(gè)問(wèn)題需要我們注意:

  • key_type可以是任何整數(shù)或字符串類型(除浮點(diǎn)類型和字節(jié)之外的任何標(biāo)量類型)。
  • 注意:枚舉不是有效的key_type。
  • value_type 可以是除另一個(gè)映射之外的任何類型。
  • Map 字段不能使用repeated關(guān)鍵字修飾。

1.9.2 map的例子

我們舉個(gè)典型的例子:學(xué)生的學(xué)科和分?jǐn)?shù)就適合用map定義:

syntax = "proto3";

package map;

option go_package = "./;score";

message Student{
int64 id = 1; //id
string name = 2; //學(xué)生姓名
map<string, int32> score = 3; //學(xué)科 分?jǐn)?shù)的map
}

運(yùn)行效果如下: 

再?gòu)?qiáng)調(diào)一下:

注意:Map 字段是不能使用repeated關(guān)鍵字修飾。

至此我們已經(jīng)掌握了ProtoBuf的所有知識(shí)點(diǎn),是不是非常簡(jiǎn)單清晰呢?

下面我們?cè)贕o項(xiàng)目中實(shí)戰(zhàn)應(yīng)用一下ProtoBuf,從ProtoBuf中讀取數(shù)據(jù),并且轉(zhuǎn)換為我們常用的結(jié)構(gòu)體

5分鐘實(shí)戰(zhàn)

1. 首先我們定義proto文件

我創(chuàng)建了一個(gè)demo目錄,創(chuàng)建了名為study_info.proto的文件

syntax = "proto3";

package demo;

option go_package = "./;study";

message StudyInfo {
int64 id = 1; //id
string name = 2; //學(xué)習(xí)的科目名稱
int32 duration = 3; //學(xué)習(xí)的時(shí)長(zhǎng) 單位秒
map<string, int32> score = 4; //學(xué)習(xí)的分?jǐn)?shù)
}

2. 生成代碼

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

protoc --go_out=. study_info.proto

3.編寫(xiě)go文件

編寫(xiě)go文件,讀取ProtoBuf中定義的字段,進(jìn)行賦值,取值,轉(zhuǎn)成結(jié)構(gòu)體等操作:

proto編碼和解碼的操作和json是非常像的,都使用“Marshal”和“Unmarshal”關(guān)鍵字。

package main

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

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

//常規(guī)賦值
studyInfo.Id = 1
studyInfo.Name = "學(xué)習(xí)ProtoBuf"
studyInfo.Duration = 180

//在go中聲明實(shí)例化map賦值給ProtoBuf消息中定義的map
score := make(map[string]int32)
score["實(shí)戰(zhàn)"] = 100
studyInfo.Score = score

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

//轉(zhuǎn)成二進(jìn)制文件
marshal, err := proto.Marshal(studyInfo)
if err != nil {
return
}
fmt.Printf("Marshal轉(zhuǎn)成二進(jìn)制結(jié)果:%v\n", marshal)

//將二進(jìn)制文件轉(zhuǎn)成結(jié)構(gòu)體
newStudyInfo := study.StudyInfo{}
err = proto.Unmarshal(marshal, &newStudyInfo)
if err != nil {
return
}
fmt.Printf("二進(jìn)制轉(zhuǎn)成結(jié)構(gòu)體的結(jié)果:%v\n", &newStudyInfo)
}

運(yùn)行結(jié)果如下:

本文總結(jié)

ProtoBuf作為開(kāi)發(fā)微服務(wù)必選的數(shù)據(jù)交換協(xié)議,基于二進(jìn)制傳輸,比json/xml更小,速度更快,使用也非常的簡(jiǎn)單。

通過(guò)這篇文章,我們不僅學(xué)會(huì)了ProtoBuf的入門(mén)操作,還使用Go語(yǔ)言基于ProtoBuf編碼解碼了數(shù)據(jù),進(jìn)行了實(shí)戰(zhàn)。

進(jìn)階部分帶大家了解了ProtoBuf如何定義消息、ProtoBuf和Go數(shù)據(jù)類型的映射、枚舉類型如何使用、通過(guò)消息嵌套復(fù)用代碼、使用map類型時(shí)需要注意的問(wèn)題和小技巧。

本文轉(zhuǎn)載自微信公眾號(hào)「 程序員升級(jí)打怪之旅」,作者「王中陽(yáng)Go」,可以通過(guò)以下二維碼關(guān)注。

轉(zhuǎn)載本文請(qǐng)聯(lián)系「 程序員升級(jí)打怪之旅」公眾號(hào)。

責(zé)任編輯:武曉燕 來(lái)源: 程序員升級(jí)打怪之旅
相關(guān)推薦

2022-04-08 09:01:14

CSS自定義屬性前端

2024-06-27 10:50:01

2020-11-17 09:32:57

設(shè)計(jì)模式責(zé)任鏈

2020-05-11 14:35:11

微服務(wù)架構(gòu)代碼

2021-09-27 07:39:52

Go初始化函數(shù)package

2022-09-21 16:56:16

設(shè)計(jì)模式微服務(wù)架構(gòu)

2023-11-21 08:37:09

2020-10-29 08:55:04

微服務(wù)

2022-12-20 07:39:46

2023-11-20 08:18:49

Netty服務(wù)器

2023-12-21 17:11:21

Containerd管理工具命令行

2023-11-06 08:16:19

APM系統(tǒng)運(yùn)維

2021-05-29 10:11:00

Kafa數(shù)據(jù)業(yè)務(wù)

2023-07-31 08:18:50

Docker參數(shù)容器

2022-11-11 19:09:13

架構(gòu)

2024-03-26 00:17:51

Go語(yǔ)言IO

2022-02-24 07:34:10

SSL協(xié)議加密

2023-11-08 08:15:48

服務(wù)監(jiān)控Zipkin

2023-10-27 08:15:45

2024-05-22 09:45:49

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)