脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Golang安装和使用protocol-buffer流程介绍

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

2022-11-17 12:28whynogome Golang

这篇文章主要介绍了Golang安装和使用protocol-buffer过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

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相关的需要加密的字段

?
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
//引入其他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中语言的编译插件,如下

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

我们以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

报错

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

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

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

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

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

调用proto

?
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
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文件,代码如下

?
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/u012830303/article/details/126517355

延伸 · 阅读

精彩推荐
  • GolangGolang中runtime的使用详解

    Golang中runtime的使用详解

    这篇文章主要介绍了Golang中runtime的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    田飞雨8162020-05-27
  • Golang修改并编译golang源码的操作步骤

    修改并编译golang源码的操作步骤

    这篇文章主要介绍了修改并编译golang源码的操作步骤,本文给大家介绍的非常详细,需要的朋友可以参考下...

    亚楠老猎人10932021-08-17
  • Golanggolang如何通过viper读取config.yaml文件

    golang如何通过viper读取config.yaml文件

    这篇文章主要介绍了golang通过viper读取config.yaml文件,围绕golang读取config.yaml文件的相关资料展开详细内容,需要的小伙伴可以参考一下...

    峰啊疯了6082022-09-07
  • Golang详解Golang 中的并发限制与超时控制

    详解Golang 中的并发限制与超时控制

    这篇文章主要介绍了详解Golang 中的并发限制与超时控制,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...

    freedomkk_qfeng4812020-05-14
  • GolangGo Excelize API源码解读GetSheetViewOptions与SetPageLayout

    Go Excelize API源码解读GetSheetViewOptions与SetPageLayout

    这篇文章主要为大家介绍了Go Excelize API源码解读GetSheetViewOptions与SetPageLayout方法示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步...

    丘山子5672022-11-10
  • Golanggo select编译期的优化处理逻辑使用场景分析

    go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go selec...

    yuchenfw8962021-08-11
  • GolangGO语言实现标题闪烁效果

    GO语言实现标题闪烁效果

    这篇文章主要介绍了GO语言实现标题闪烁效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参...

    陶士涵2222020-08-02
  • Golanggo语言接口用法实例分析

    go语言接口用法实例分析

    这篇文章主要介绍了go语言接口用法,实例分析了Go语言接口的定义及使用技巧,需要的朋友可以参考下 ...

    两把刷子2202020-04-20