Golang安装和使用protocol-buffer流程介绍

目录
  • 前言
  • 安装protoc编译工具
  • 编写proto文件
  • 生成指定语言的proto文件
  • 调用proto
  • 制作插件

前言

protocol buffer是Google发布的一种独立的数据交换格式,类似于json,用于数据的序列化和解析。不同点是不能直接在各编程语言中使用,需要先在一个proto文件中定义需要传输的数据格式,然后使用proto工具把proto文件编译成想要的语言,如java、go、php等。然后在代码中,使用语言对应的protocol buffer包调用proto工具生成的文件,完成数据的序列化

安装protoc编译工具

首先安装protoc编译工具,在http://github.com/google/protobuf/releases,根据自己的系统选择包,下载后解压。我是win10 64,所以选择protoc-21.4-win64.zip,解压后放在了E盘。

添加环境变量,让系统命令可以识别protoc命令。

配置环境变量

PB_PATH=E:\protoc-3.21.4-win64

PATH=%PB_PATH%\bin

添加后,在命令行执行 protoc,返回信息,表示安装成功

编写proto文件

创建一个user.proto文件,该文件下可以定义一些user相关的需要加密的字段

//引入其他proto文件,就可以使用该文件中定义的message类型,定义本文件中的message下的字段
import "myproject/other_protos.proto";
// 指定的当前proto语法的版本
syntax = "proto3";
// 指定生成出来的文件的package,用来避免同一个文件调同名的消息时冲突
// 包说明符对生成代码的影响取决于您选择的语言,php会生成命名空间,go会生成package
package service;
//根据编程语言不同,参数名不同
//下面代码指定了生成go文件的生成目录,文件的包名。不设置包名时,包名默认为go文件所在目录名。会覆盖package设置的go的包名
//路径以执行命令的目录为当前目录,寻找相对路径
option go_package="./;service";
//定义消息类型,通过关键字message字段指定的,消息就是需要传输的数据格式的定义
message User {
// required必传;optional 可选;repeated 可重复初入。不写前缀默认为required
// 字段名后的数字不是默认值,是标识。标识号是[0,2^29-1]范围内的一个整数。[1-15]内的标识号在编码时只占用一个字节,包含标识符和字段类型,[16-2047]之间的标识符占用2个字节。建议为频繁出现的字段使用[1-15]间的标识符。
//字段类型除了除了常规类型,也可使用import引入文件中定义的message类型
	required string sername = 1;
	optional int32 age = 2;
	repeated int32 height = 3;
}
//定义服务类型,用作rpc通讯
service SearchService {
    //rpc 服务的函数名 (传入参数)返回(返回参数)
    //传入和返回参数,需要使用proto中定义的message
    rpc Search (SearchRequest) returns (SearchResponse);
}

生成指定语言的proto文件

protoc内置9中语言的编译插件,如下

我们以go为例。protoc没有内置go的编译器,需要引入外部插件protoc-gen-go

我们使用命令

go get github.com/golang/protobuf/protoc-gen-go

下载并编译了包,包中有main.go文件,所以在GOPATH/bin目录下生成了可执行文件protoc-gen-go.exe,这个文件就是我们在protoc中用到的插件

我们也可以自定义自己的插件。插件名必须以protoc-gen-插件名.exe命名

在调用时以:插件_out=参数 调用,之后回讲到如何制作插件

我们以go_out插件为例,执行命令

命令行后的第一个参数为输出目录。但是我们已经在go_package中指定了目录,所以第一个参数是无效的,填当前目录.即可。第二个参数是,要编译的文件路径和文件名(以命令执行目录为当前目录的相对路径)

protoc后可以追加1个或多个 -I=path 指定解析import指令时要在其中查找.proto文件的目录,若文件没有使用import,则不需要该参数

protoc --go_out=. proto_type/hello.proto

报错

protoc默认会从环境变量path下的路径中寻找插件,没找到报错。

查看发现path中只配置了go的安装GOROOT目录下的bin,而protoc-gen-go.exe在GOPATH的bin目录下

我们把protoc-gen-go.exe复制到GOROOT的bin目录下,即可。或者在环境变量path中添加gopath/bin目录。再次执行命令即可

调用proto

import (
	"fmt"
	"github.com/golang/protobuf/proto"
)
func main() {
	//定义一个要加密的变量
	msg := &protoc_type.User{
		Username:"zhangsan",
		Age:12,
	}
	//加密
	marshal,err := proto.Marshal(msg)
	if err!=nil{
		fmt.Println(err.Error())
	}else{
		fmt.Println(marshal)
	}
	//定义一个变量,用于接收解码的值,类型必须用加密的值类型一样
	unmarshal := &protoc_type.User{}
	//解码
	err2 := proto.Unmarshal(marshal,unmarshal)
	if err2 == nil{
		fmt.Println(unmarshal)
		fmt.Println(unmarshal.Username)
		fmt.Printf("%T",unmarshal)
	}
}

制作插件

我们上文中提到,生成go用到了一个protoc的外部插件。插件的工作原理其实就是读取proto文件内容,并根据文件内容生成指定文件。文件中保存了定义的字段类型和方法之间的关系,以便编码和解码时使用

当我们在proto文件中定义了service,说明我们需要用到rpc服务。protoc内置的插件和一些官方的外部插件,只是提供了service中方法与参数类型的绑定,用于校验调用方法时类型是否正确。在service中定义的方法,我们需要自己创建。

当方法较多时,我们需要打开proto文件,一一对应的创建func,效率不高也容易出错。这种情况下,我们制作一个插件,读取service内容,自动创建方法,方法内的代码之后根据业务需求填充即可。

go官方提供了一个包,可以自动解析读取命令行传入proto文件,代码如下

package main
import (
	"fmt"
	"strings"
	"google.golang.org/protobuf/compiler/protogen"
)
type rpc struct{}
func main() {
	g := rpc{}
	protogen.Options{}.Run(g.Generate)
}
// Generate generate service code
func (md *rpc) Generate(plugin *protogen.Plugin) error {
	//遍历读取的命令行中传入的proto文件
	for _, f := range plugin.Files {
		//如果文件中没有定义service,跳过
		if len(f.Services) == 0 {
			continue
		}
		//根据proto文件名,生成一个自定义的文件,保存该proto中定义的func
		fileName := f.GeneratedFilenamePrefix + ".svr.go"
		//把该文件保存在proto文件的所在目录
		t := plugin.NewGeneratedFile(fileName, f.GoImportPath)
		//写入文字
		t.P("// Code generated by protoc-gen-tinyrpc.")
		//写入空行
		t.P()
		//写入包名
		pkg := fmt.Sprintf("package %s", f.GoPackageName)
		t.P(pkg)
		t.P()
		//遍历一个文件下所有service,自动生成方法
		for _, s := range f.Services {
			//插入注释,定义service类型
			serviceCode := fmt.Sprintf(`%stype %s struct{}`,
				getComments(s.Comments), s.Desc.Name())
			t.P(serviceCode)
			t.P()
			//遍历一个service下的方法,生成方法
			for _, m := range s.Methods {
				funcCode := fmt.Sprintf(`%sfunc(this *%s) %s(args *%s,reply *%s)error{
					// define your service ...
					return nil
				}
				`, getComments(m.Comments), s.Desc.Name(),
					m.Desc.Name(), m.Input.Desc.Name(), m.Output.Desc.Name())
				t.P(funcCode)
			}
		}
	}
	return nil
}
// getComments get comment details
func getComments(comments protogen.CommentSet) string {
	c := make([]string, 0)
	c = append(c, strings.Split(string(comments.Leading), "\n")...)
	c = append(c, strings.Split(string(comments.Trailing), "\n")...)
	res := ""
	for _, comment := range c {
		if strings.TrimSpace(comment) == "" {
			continue
		}
		res += "//" + comment + "\n"
	}
	return res
}

执行 go install 编译该文件,生成exe文件,文件名称设置为protoc-gen-gofunc.exe。然后再次执行protoc命令,如下

protoc --go_out=. proto_type/hello.proto --gofunc_out=. proto_type/hello.proto

执行后,会在proto_type目录下生成两个文件,hello.pd.go, hello.srv.go

到此这篇关于Golang安装和使用protocol-buffer方法介绍的文章就介绍到这了,更多相关Golang protocol-buffer内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang Protocol Buffer案例详解

    Golang Protocol Buffer教程 本文介绍如何在Go应用中利用Protocol Buffer数据格式.主要包括什么是Protocol Buffer数据格式,其超越传统数据格式XML或JSON的优势是什么. 1. Protocol Buffer数据格式 Protocol Buffer,本质就是一种数据格式,和JSON或XML一样,不同的语言用于结构化数据序列化或反序列化.该数据格式的优势是较xml或json更小,源于Google.假如我们有一个对象,我们用三种数据结构进行表示: <

  • Golang安装和使用protocol-buffer流程介绍

    目录 前言 安装protoc编译工具 编写proto文件 生成指定语言的proto文件 调用proto 制作插件 前言 protocol buffer是Google发布的一种独立的数据交换格式,类似于json,用于数据的序列化和解析.不同点是不能直接在各编程语言中使用,需要先在一个proto文件中定义需要传输的数据格式,然后使用proto工具把proto文件编译成想要的语言,如java.go.php等.然后在代码中,使用语言对应的protocol buffer包调用proto工具生成的文件,完成

  • Golang分布式锁简单案例实现流程

    其实锁这种东西,都能能不加就不加,锁会导致程序一定程度上退回到串行化,进而降低效率. 首先,看一个案例,如果要实现一个计数器,并且是多个协程共同进行的,就会出现以下的情况: package main import ( "fmt" "sync" ) func main() { numberFlag := 0 wg := new(sync.WaitGroup) for i := 0; i < 200; i++ { wg.Add(1) go func() { def

  • winserver 2012R2 安装oracle及创建表流程(推荐)

    一.安装oracle11g数据库: 在Winserver 2012 R2 操作系统安装oracle数据库前,需要先启动系统本地的.net framework3.5服务功能,注意不是下载.net framework3.5安装,而是系统自带该服务,只不过处于禁用状态.启动服务器.net framework3.5的具体步骤:服务器管理器-->添加功能和角色-->下一步,默认至功能项,选择启动.net framework3.5即可. 接下来,按常规步骤安装oracle. 二.oracle自定义表空间及

  • python flask sqlalchemy连接数据库流程介绍

    1.安装flask_sqlalchemy和pymysql包 pip install flask-sqlalchemy pip install pymysql 2.进行配置 使用Flask-SQLAlchemy扩展操作数据库,首先需要通过URL建立数据库连接,必须保存到Flask配置对象的SQLALCHEMY_DATABASE_URI中. HOSTNAME = '127.0.0.1'PORT     = '3306'DATABASE = 'flask_test'USERNAME = 'root'P

  • Redux模块化拆分reducer函数流程介绍

    目录 1. 概述 2. 方式1-单纯文件拆分 3. 方式2-使用中间件redux-thunk进行模块拆分 1. 概述 使用 redux 库中提供的 combineReducers 方法,可以将多个拆分 reducer 函数合并成统一的 reducer 函数,提供给 createStore 来使用.我们可以将 Redux 进行模块化拆分,再利用这个函数,将多个拆分 reducer 函数合并成统一的 reducer 函数,再传给 createStore 来使用. 2. 方式1-单纯文件拆分 redu

  • golang RPC包原理和使用详细介绍

    目录 工作流程 工作模式 http模式 服务器模式 本篇文章旨在通过学习rpc包和github上的一个rpc小项目,熟悉和学习golang中各个包的使用 工作流程 通过阅读官方文档,了解了rpc的基本工作模式 第一步,建立一个用于远程调用的包,存放仅供远程调用使用的方法和类型- 第二步,实例化包的对象,并在rpc中注册该包,以便之后的调用 第三步,建立一个服务端,接收客户端的请求,使用编码器解析请求后,根据请求中的方法和参数,调用第二步注册的实例的方法,然后使用编码器把返回值加密后,返回给客户端

  • JavaScript自定义Webpack配置实现流程介绍

    目录 1 初始化并创建要被打包的文件 2 命令行配置 3 配置文件配置 1 初始化并创建要被打包的文件 首先创建文件夹webpack-demo(随便起一个),用来演示打包过程.在该文件夹下终端运行命令,对项目进行初始化操作,对包进行管理: npm init # 输入命令后一直敲回车即可 npm init -y # -y是yes的意思,在初始化时省去敲回车的步骤 命令成功运行之后,该文件夹下出现了package.json文件,文件内容具体如下: { "name": "webpa

  • YUM解决RPM包安装依赖关系及yum工具介绍本地源配置方法详解

    1.背景概述 在实际生产环境下,对于在linux系统上安装rpm包,主要面临两个实际的问题 1)安装rpm包过程中,不断涌现的依赖关系问题,导致需要按照提示或者查询资料,手工安装更多的包 2)由于内外网的隔离,无法连接外网的yum源 鉴于上述因此,本文将详细介绍,yum工具以及配置本地yum源的方法 2.yum工具简介 •yum工具作为rpm包的软件管理器,可以进行rpm包的安装.升级以及删除等日常管理工作,而且对于rpm包之间的依赖关系可以自动分析,大大简化了rpm包的维护成本. •yum工具

  • Protocol Buffer技术深入理解(C++实例)

    这篇Blog仍然是以Google的官方文档为主线,代码实例则完全取自于我们正在开发的一个Demo项目,通过前一段时间的尝试,感觉这种结合的方式比较有利于培训和内部的技术交流.还是那句话,没有最好的,只有最适合的.我想写Blog也是这一道理吧,不同的技术主题可能需要采用不同的风格.好了,还是让我们尽早切入主题吧. 一.生成目标语言代码 下面的命令帮助我们将MyMessage.proto文件中定义的一组Protocol Buffer格式的消息编译成目标语言(C++)的代码.至于消息的内容,我们会在后

随机推荐