go语言的gRPC教程-protobuf基础

一、前言

RPC,全称Remote Procedure Call,中文译为远程过程调用。通俗地讲,使用RPC进行通信,调用远程函数就像调用本地函数一样,RPC底层会做好数据的序列化与传输,从而能使我们更轻松地创建分布式应用和服务。

gRPC是谷歌开源的一款高性能、支持多种开发语言的服务框架,对于一个rpc我们关注如下几方面:

序列化协议gRPC使用protobuf,首先使用protobuf定义服务,然后使用这个文件来生成客户端和服务端的代码。因为pb是跨语言的,因此即使服务端和客户端语言并不一致也是可以互相序列化和反序列化的

网络传输层。gRPC使用http2.0协议,http2.0相比于HTTP 1.x ,大幅度的提升了 web 性能。
在这里插入图片描述

二、Protobuf IDL

所谓序列化通俗来说就是把内存的一段数据转化成二进制并存储或者通过网络传输,而读取磁盘或另一端收到后可以在内存中重建这段数据

1、protobuf协议是跨语言跨平台的序列化协议。

2、protobuf本身并不是和gRPC绑定的。它也可以被用于非RPC场景,如存储等

json xml都是一种序列化的方式,只是他们不需要提前预定义idl,且具备可读性,当然他们传输的体积也因此较大,可以说是各有优劣

所以先来介绍下protobuf的idl怎么写。protobuf最新版本为proto3,在这里你可以看到详细的文档说明:https://protobuf.dev/programming-guides/proto3/

2.1、定义消息类型

protobuf里最基本的类型就是message,每一个messgae都会有一个或者多个字段(field),其中字段包含如下元素

在这里插入图片描述

  • 类型:类型不仅可以是标量类型(intstring等),也可以是复合类型(enum等),也可以是其他message

  • 字段名:字段名比较推荐的是使用下划线/分隔名称

  • 字段编号:一个messgae内每一个字段编号都必须唯一的,在编码后其实传递的是这个编号而不是字段名

  • 字段规则:消息字段可以是以下字段之一

    • singular:格式正确的消息可以有零个或一个字段(但不能超过一个)。使用 proto3 语法时,如果未为给定字段指定其他字段规则,则这是默认字段规则
    • optional:与 singular 相同,不过您可以检查该值是否明确设置
    • repeated:在格式正确的消息中,此字段类型可以重复零次或多次。系统会保留重复值的顺序
    • map:这是一个成对的键值对字段
  • 保留字段:为了避免再次使用到已移除的字段可以设定保留字段。如果任何未来用户尝试使用这些字段标识符,编译器就会报错

2.2、标量值类

标量类型会涉及到不同语言和编码方式:

.proto TypeGo TypeNotes
doublefloat64
floatfloat32
int32int32使用可变长度的编码。对负数的编码效率低下 - 如果您的字段可能包含负值,请改用 sint32。
int64int64使用可变长度的编码。对负数的编码效率低下 - 如果字段可能有负值,请改用 sint64。
uint32uint32使用可变长度的编码。
uint64uint64使用可变长度的编码。
sint32int32使用可变长度的编码。有符号整数值。与常规 int32 相比,这些函数可以更高效地对负数进行编码。
sint64int64使用可变长度的编码。有符号整数值。与常规 int64 相比,这些函数可以更高效地对负数进行编码。
fixed32uint32始终为 4 个字节。如果值通常大于 2^28,则比 uint32 更高效。
fixed64uint64始终为 8 个字节。如果值通常大于 2^56,则比 uint64 更高效。
sfixed32int32始终为 4 个字节。
sfixed64int64始终为 8 个字节。
boolbool
stringstring字符串必须始终包含 UTF-8 编码或 7 位 ASCII 文本,并且长度不得超过 232。
bytes[]byte可以包含任意长度的 2^32 字节。

2.3、复合类型

(1)数组
message SearchResponse {repeated Result results = 1;
}message Result {string url = 1;string title = 2;repeated string snippets = 3;
}
(2)枚举
message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;enum Corpus {UNIVERSAL = 0;WEB = 1;IMAGES = 2;LOCAL = 3;NEWS = 4;PRODUCTS = 5;VIDEO = 6;}Corpus corpus = 4;
}
(3)服务

定义的method仅能有一个入参和出参数。如果需要传递多个参数需要定义成message

service SearchService {rpc Search(SearchRequest) returns (SearchResponse);
}

2.4、使用其他消息类型

使用import引用另外一个文件的pb

syntax = "proto3";import "google/protobuf/wrappers.proto";package ecommerce;message Order {string id = 1;repeated string items = 2;string description = 3;float price = 4;google.protobuf.StringValue destination = 5;
}

三、protoc使用

protoc就是protobuf的编译器,它把proto文件编译成不同的语言

3.1、安装

https://github.com/google/protobuf/releases
在这里插入图片描述

  • Windows 下载压缩包解压,并添加解压路径中的 bin 文件夹路径到环境变量Path中,新开终端 protoc --version验证安装。

  • Linux, using apt or apt-get, for example:

$ apt install -y protobuf-compiler
$ protoc --version  # Ensure compiler version is 3+
  • MacOS, using Homebrew:
$ brew install protobuf
$ protoc --version  # Ensure compiler version is 3+

3.2、使用

$ protoc --help
Usage: protoc [OPTION] PROTO_FILES-IPATH, --proto_path=PATH   #指定搜索路径--plugin=EXECUTABLE: # 指定要使用的插件可执行文件。通常,protocol会在PATH中搜索插件....--cpp_out=OUT_DIR           Generate C++ header and source.--csharp_out=OUT_DIR        Generate C# source file.--java_out=OUT_DIR          Generate Java source file.--js_out=OUT_DIR            Generate JavaScript source.--objc_out=OUT_DIR          Generate Objective C header and source.--php_out=OUT_DIR           Generate PHP source file.--python_out=OUT_DIR        Generate Python source file.--ruby_out=OUT_DIR          Generate Ruby source file@<filename>                #proto文件的具体位置
(1) 搜索路径参数

第一个比较重要的参数就是搜索路径参数,即上述展示的-IPATH, --proto_path=PATH。它表示的是我们要在哪个路径下搜索.proto文件,这个参数既可以用-I指定,也可以使用--proto_path=指定。

如果不指定该参数,则默认在当前路径下进行搜索;另外,该参数也可以指定多次,这也意味着我们可以指定多个路径进行搜索。

(2) 语言插件参数

语言参数即上述的--cpp_out=--python_out=等,protoc支持的语言长达13种,且都是比较常见的

运行help出现的语言参数,说明protoc本身已经内置该语言对应的编译插件,我们无需安装

LanguageGenerated CodeSource
C++ (include C++ runtime and protoc)C++src
JavaJavajava
PythonPythonpython
Objective-CObjective-Cobjectivec
C#C#csharp
RubyRubyruby
PHPPHPphp

下面的语言是由google维护,通过protoc的插件机制来实现,所以仓库单独维护

  • Dart
  • Go
(3) proto文件位置参数

proto文件位置参数即上述的@<filename>参数,指定了我们proto文件的具体位置,如proto1/greeter/greeter.proto

3.3、 语言插件

(1) golang插件

非内置的语言支持就得自己单独安装语言插件,比如--go_out=对应的是protoc-gen-go,安装命令如下:

# 最新版
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest# 指定版本
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.3.0

可以使用下面的命令来生成代码

$ protoc --proto_path=src --go_out=. --go_opt=paths=source_relative foo.proto bar/baz.proto
注意

protoc-gen-go要求pb文件必须指定go包的路径,即

option go_package = ".;streaming";
----proto_path

这个选项用于指定 protoc 编译器在查找 .proto 文件时应该搜索的根目录。当你在 .proto 文件中使用 import 语句导入其他 .proto 文件时,编译器需要知道去哪里找到这些被导入的文件

–go_out

指定go代码生成的基本路径

–go_opt:设定插件参数

protoc-gen-go提供了 --go_opt 来为其指定参数,并可以设置多个

1、如果使用 paths=import , 生成的文件会按go_package路径来生成,当然是在--go_out目录下,即

$go_out/$go_package/pb_filename.pb.go

2、如果使用 paths=source_relative , 就在当前pb文件同路径下生成代码。注意pb的目录也被包含进去了。即

$go_out/$pb_filedir/$pb_filename.pb.go
(2) grpc go插件

google.golang.org/protobuf中,protoc-gen-go纯粹用来生成pb序列化相关的文件,不再承载gRPC代码生成功能。

生成gRPC相关代码需要安装grpc-go相关的插件protoc-gen-go-grpc

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

执行code gen命令

$ protoc --go_out=. --go_opt=paths=source_relative \--go-grpc_out=. --go-grpc_opt=paths=source_relative \routeguide/route_guide.proto
–go-grpc_out

指定grpc go代码生成的基本路径

命令会产生如下文件

  • route_guide.pb.go: protoc-gen-go的产出物,包含所有类型的序列化和反序列化代码

  • route_guide_grpc.pb.go: protoc-gen-go-grpc的产出物,包含

    • 定义在 RouteGuide service中的用来给client调用的接口定义
    • 定义在 RouteGuide service中的用来给服务端实现的接口定义
–go-grpc_opt

protoc-gen-go类似,protoc-gen-go-grpc提供 --go-grpc_opt 来指定参数,并可以设置多个

github.com/golang/protobuf vs google.golang.org/protobuf

github.com/golang/protobuf虽然已经废弃,但网上搜索时经常还能搜到,方便理解整理两者区别。

代码差异

这两个库,google.golang.org/protobufgithub.com/golang/protobuf的升级版本,v1.4.0之后github.com/golang/protobuf仅是google.golang.org/protobuf的包装

功能差异

google.golang.org/protobuf,纯粹用来生成pb序列化相关的文件,不再承载gRPC代码生成功能。生成gRPC相关代码需要安装grpc-go相关的插件protoc-gen-go-grpc

github.com/golang/protobuf ,可以同时生成pb和gRPC相关代码的

用法差异

google.golang.org/protobuf

$ protoc --go_out=. --go_opt=paths=source_relative \--go-grpc_out=. --go-grpc_opt=paths=source_relative \routeguide/route_guide.proto

github.com/golang/protobuf

$ protoc --go_out=plugins=grpc,paths=import:. \routeguide/route_guide.proto

--go_out的写法是,参数之间用逗号隔开,最后加上冒号来指定代码的生成位置,比如--go_out=plugins=grpc,paths=import:.

--go_out主要的两个参数为pluginspaths,分别表示生成Go代码所使用的插件,以及生成的Go代码的位置。

plugins参数有不带grpc和带grpc两种,两者的区别如下,带grpc的会多一些跟gRPC相关的代码,实现gRPC通信

paths参数有两个选项,分别是 importsource_relative,默认为 import

  • import表示按照生成的Go代码的包的全路径去创建目录层级
  • source_relative 表示按照 proto源文件的目录层级去创建Go代码的目录层级,如果目录已存在则不用创建。

总之,用google.golang.org/protobuf就对了!

Buf 工具

可以看到使用protoc的时候,当使用的插件逐渐变多,插件参数逐渐变多时,命令行执行并不是很方便和直观。例如后面使用到了grpc-gateway+swagger插件时

$ protoc -I ./pb \--go_out ./ecommerce --go_opt paths=source_relative \--go-grpc_out ./ecommerce --go-grpc_opt paths=source_relative \--grpc-gateway_out ./ecommerce --grpc-gateway_opt paths=source_relative \--openapiv2_out ./doc --openapiv2_opt logtostderr=true \./pb/ecommerce/v1/product.proto

其次依赖某些外部的protobuf文件时,只能通过拷贝到本地的方式,也不够方便

因此诞生了✨ Buf 这个项目,它除了能解决上述问题,还有额外的功能

  • 不兼容破坏检查
  • linter
  • 集中式的版本管理

初始化模块

在pb文件的根目录执行,为这个pb目录创建一个buf的模块。此后便可以使用buf的各种命令来管理这个buf模块了

$ buf mod init

此时会在根目录多出一个buf.yaml文件,内容为

# buf.yaml
version: v1
breaking:use:- FILE
lint:use:- DEFAULT

Lint pb文件

$ buf lint
ecommerce/v1/product.proto:10:9:Service name "ServiceOrderManagement" should be suffixed with "Service".
ecommerce/v1/product.proto:11:18:RPC request type "getOrderReq" should be named "GetOrderRequest" or "ServiceOrderManagementGetOrderRequest".

调整lint规则

 # buf.yamlversion: v1breaking:use:- FILElint:use:- DEFAULT
+  except:
+    - PACKAGE_VERSION_SUFFIX
+    - FIELD_LOWER_SNAKE_CASE
+    - SERVICE_SUFFIX

生成代码

插件:和使用protoc一样,该装的插件一样要装

插件模版

创建一个buf.gen.yaml ,它是buf生成代码的配置。上面的protoc同等功能的buf.gen.yaml可以写成如下形式,相对protoc更加直观

# buf.gen.yaml
version: v1
plugins:- plugin: goout: ecommerceopt:- paths=source_relative- plugin: go-grpcout: ecommerceopt:- paths=source_relative- name: grpc-gatewayout: ecommerceopt:- paths=source_relative- generate_unbound_methods=true- name: openapiv2out: docopt:- logtostderr=true

生成代码

buf generate pb

buf generate 命令将会

  • 搜索每一个buf.yaml配置里的所有protobuf文件
  • 复制所有protobuf文件到内存
  • 编译所有protobuf文件
  • 执行模版文件里的每一个插件

添加依赖

在使用grpc-gateway时依赖了google.api.http,在不使用buf的场景,我们需要手动复制.proto到本地。

buf为我们提供了 Buf Schema Registry (BSR),除了可以使用其他人发布的模块,也可以把我们自己的模块发布到BSR

在模块的文件里声明依赖项

 # buf.yamlversion: v1breaking:use:- FILElint:use:- DEFAULT
+deps:
+  - buf.build/googleapis/googleapis

然后执行

buf mod update

buf mod update 把你所有的 deps 更新到最新版。并且会生成 buf.lock 来固定版本

# Generated by buf. DO NOT EDIT.
version: v1
deps:- remote: buf.buildowner: googleapisrepository: googleapiscommit: 75b4300737fb4efca0831636be94e517

此时执行buf generate pb 即使本地没有依赖,也不会再报错缺少依赖了

参考

  • Buf 官方文档
  • Protocol Buffers Documentation
  • https://segmentfault.com/a/1190000043353574

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux基本指令,对路径的认识

引言简单介绍一些Linux的基本指令&#xff0c;快速上手Linux操作系统。一、ls指令语法&#xff1a;ls [选项] [目录或文件]功能&#xff1a;&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件件&#xff0c;将列出文件名以及其他信息常用选项&a…

25. html 使用的字符集是什么,有什么特点

总结 utf-8&#xff0c;支持所有语言一、HTML 默认使用的字符集✅ HTML 页面推荐使用 UTF-8 字符集<meta charset"UTF-8" />这是 HTML5 中推荐的标准字符编码&#xff0c;用于定义网页中字符的编码方式。二、什么是字符集&#xff08;Character Encoding&#…

MySQL 读写分离(含示例代码)

背景 面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性…

C#中Visual Studio平台按照OfficeOpenXml步骤

找到包的地址&#xff1a; NuGet Gallery | DocumentFormat.OpenXml.Framework 3.3.0 https://nuget.info/packages 报错&#xff1a; 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 无法解析依赖项“EPPlus”。使用的源: Officeopenxml, Mic…

【Linux】重生之从零开始学习运维之备份恢复

备份恢复准备工作16主机-ubuntu系统准备日志目录mkdir -p /data/mysql/logs/ chown mysql:mysql -R /data/mysql定制日志配置vim /etc/mysql/mariadb.conf.d/50-server.cnf log_bin/data/mysql/logs/binlog systemctl restart mariadb删除db1数据库drop database db1;13主机-ub…

VoIP技术全面深度学习指南:从原理到实践的认知进化

一、VoIP技术的本质认知与历史演进 1.1 技术本质的深层理解 VoIP&#xff08;Voice over Internet Protocol&#xff0c;IP语音传输&#xff09;从根本上代表了通信技术的范式转换。这不仅仅是将模拟语音信号数字化那么简单&#xff0c;而是将传统的电路交换模式彻底转向包交换…

CentOS Nginx 1.13.9 部署文档

以下是 Nginx 1.13.9 的详细安装步骤&#xff08;基于 CentOS/Ubuntu 系统&#xff09;&#xff1a;1. 安装依赖 CentOS/RHEL sudo yum install -y gcc pcre pcre-devel zlib zlib-devel openssl openssl-develUbuntu/Debian sudo apt update && sudo apt install -y b…

CSS-in-JS 动态主题切换与首屏渲染优化

动态主题切换的实现方式1. 使用 CSS 变量&#xff08;CSS Custom Properties&#xff09;CSS 变量是实现主题切换最直接的方式&#xff1a;:root {--primary-color: #4285f4;--background-color: #ffffff;--text-color: #333333; }[data-theme"dark"] {--primary-col…

不止 “听懂”,更能 “感知”!移远通信全新AI 音频模组 重新定义智能家居“听觉”逻辑

7月29日&#xff0c;在 2025 世界人工智能大会&#xff08;WAIC&#xff09;期间&#xff0c;移远通信正式发布全新 VA500-GL AI 音频模组。该产品基于本地化 AI 算法&#xff0c;为智能家电赋予精准 “听觉” 与主动交互能力&#xff0c;借助环境状态智能检测、离线语音控制及…

【Python】 切割图集的小脚本

Python 切割图片脚本 前言&#xff1a; 有短时间没写博客了&#xff0c;今天打算再写一篇MonoGame的教程&#xff0c;这篇是我再做我自己的2D 游戏项目的时候我需要一些已经切割好的图片但我得到图片是合在一起图集&#xff0c;这个脚本适合正在做2D游戏开发且不依赖于游戏引…

网络安全是什么?手把手教你认识网络安全

网络安全是什么&#xff1f;手把手教你认识网络安全 提到网络安全&#xff0c;不少人会联想到电影里黑客指尖翻飞攻破系统的炫酷场景。但实际上&#xff0c;它并非遥不可及的技术名词&#xff0c;而是与我们日常生活息息相关的 “数字保镖”。从手机支付密码到社交账号信息&am…

AtCoder Beginner Contest 416(2025.7.26)

文章目录A Vacation ValidationB 1D Akari&#xff08;补&#xff09;C Concat (X-th)&#xff08;补&#xff09;题目考查题意简述解法思路 &#xff1a;AC代码D Match, Mod, Minimize 2&#xff08;补&#xff09;题目分数/评级题目考查时间复杂度题意简述解法思路 &#xff…

基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(五)

目录 五、Hue、Zeppelin 比较 1. Zeppelin 简介 2. Zeppelin 安装配置 &#xff08;1&#xff09;安装环境 &#xff08;2&#xff09;Zeppelin 及其相关组件 &#xff08;3&#xff09;配置 Zeppelin &#xff08;4&#xff09;启动 Zeppelin &#xff08;5&#xff0…

《消息队列学习指南:从 MQ 基础到 SpringAMQP 实践》

初识MQ 同步调用 目前我们采用的是基于OpenFeign的同步调用&#xff0c;也就是说业务执行流程是这样的&#xff1a; 支付服务需要先调用用户服务完成余额扣减 然后支付服务自己要更新支付流水单的状态 然后支付服务调用交易服务&#xff0c;更新业务订单状态为已支付 三个…

深度学习 --- 过拟合与欠拟合

深度学习 — 过拟合与欠拟合 文章目录深度学习 --- 过拟合与欠拟合一.概念1.1 过拟合1.2 欠拟合1.3 判断方式二&#xff0c;解决欠拟合三&#xff0c;解决过拟合3.1 L2正则化3.1.1 定义以及作用3.1.2 代码3.2 L1正则化3.3 L1与L2对比3.4 Dropout示例3.5 数据增强3.5.1 图片缩放…

Python 之抽象方法 @abstractmethod 的理解

如果你熟悉 Java 的话&#xff0c;Java 里有一个抽象接口的概念&#xff0c;Python 里的抽象方法基本上与其类似。在 Python 中&#xff0c;abstractmethod 是一个装饰器&#xff0c;用于定义抽象方法。它是实现抽象基类&#xff08;Abstract Base Class, ABC&#xff09;的核心…

深度学习·pytorch

广播机制 从末尾开始逐个维度遍历两个矩阵的shape&#xff0c;如果维度不相同&#xff0c;则考虑广播&#xff1a;任一方的维度为1或者维度不存在(小矩阵广播为大矩阵)&#xff0c;这样的运算可以广播 可以广播的例子 xtorch.empty(5,3,4,1) ytorch.empty(3,1,1) (x.add_(y)).s…

SpringBoot集成deepseek

pom文件&#xff1a;<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org…

JetBrains Annotations:从入门到落地,彻底告别 NullPointerException

本文基于三篇高质量博客&#xff08;JetBrains Annotations官方文档、Jakarta Validation 规范、《Effective Java》第3版&#xff09;的原文内容&#xff0c;结合作者在一线研发团队落地 JetBrains Annotations 的实战经验&#xff0c;系统梳理了该注解库的核心能力、使用姿势…

基于Rust与HDFS、YARN、Hue、ZooKeeper、MySQL

基于Rust与HDFS、YARN、Hue、ZooKeeper、MySQL集合 以下是基于Rust与HDFS、YARN、Hue、ZooKeeper、MySQL等技术栈结合的实例,涵盖不同场景和应用方向: 数据处理与分析 使用Rust编写MapReduce作业,通过YARN提交到HDFS处理大规模数据集。Rust的高性能特性适合处理密集型计算…