热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

go如何从零编写protoBuf插件

go如何从零编写protoBuf插件-本期的主要内容将手把手教会大家,编写probuf的go插件,以我自己编写的一个生成结构体的插件为例子。项目位于https:github.com

本期的主要内容将手把手教会大家,编写probuf的go插件,以我自己编写的一个生成结构体的插件为例子。项目位于 https://github.com/hisheng/pr...

一、自定义ProtoBuf插件介绍

我们常用的go支持protobuf插件有

插件名称介绍
protoc-gen-go通过.proto文件生成.pb.go文件
protoc-gen-doc通过.proto文件生成文档
protoc-gen-go-errors通过.proto文件生成error
protoc-gen-go-errors通过.proto文件生成error
protoc-gen-go-grpc通过.proto文件生成grpc
protoc-gen-go-http通过.proto文件生成http
protoc-gen-openapi通过.proto文件生成openapi
protoc-gen-validate通过.proto文件生成validate验证
protoc-gen-go-enum通过.proto文件生成go自定义枚举

基本上该有的插件,都已经有写过了,所以我们可以查看别人的源码,来观察怎么来写一个插件。

二、手把手写自定义插件(以protoc-gen-go-struct为例)

2.1 新建protoc-gen-go-struct文件夹,并且进入到这个文件夹里。

mkdir protoc-gen-go-struct && cd protoc-gen-go-struct

2.2 新建go module项目

我们执行 go mod init modName 命名来生成项目如下:

go mod init github/hisheng/protoc-gen-go-struct

此时我们查看文件夹发现生产了一个 go.mod文件,查看一下代码如下:

module github/hisheng/protoc-gen-go-struct

go 1.19

2.3 写main函数

我们在项目根目录,写一个main.go如下

touch main.go

此时我们发现项目根目录下,生成了一个main.go文件。
我们写一个main函数,代码如下:

package main

import (
    "google.golang.org/protobuf/compiler/protogen"
)

func main() {
    protogen.Options{}.Run(func(gen *protogen.Plugin) error {
        for _, f := range gen.Files {
            if !f.Generate {
                continue
            }
            generateFile(gen, f)
        }
        return nil
    })
}

这个main()函数大家可以直接复制,基本所有的插件都是这样的格式,当然这个main也可以接受参数,这里我们简化,先不介绍,感兴趣的人,可以参考protoc-gen-go的main函数来写。
我们自己写的方法主要是 generateFile(gen, f) 这个函数。
这个函数用来读取.proto文件,并且生产go文件。

2.4 自定义generateFile(gen, f)函数

这个函数全称是 generateFile(gen protogen.Plugin, file protogen.File),接受的两个参数

gen *protogen.Plugin 为生成的插件,主要用来生成go文件
file *protogen.File 为.proto文件对他的file对象

我这里的主要代码是:

// 生成.struct.go文件,参数为 输出插件gen,以及读取的文件file
func generateFile(gen *protogen.Plugin, file *protogen.File) {
    filename := file.GeneratedFilenamePrefix + ".struct.go"
    g := gen.NewGeneratedFile(filename, file.GoImportPath)
    // 输出 package packageName
    g.P("package ", file.GoPackageName)
    g.P() // 换行

    for _, m := range file.Messages {
        // 输出 type m.GoIdent struct {
        g.P("type ", m.GoIdent, " struct {")
        for _, field := range m.Fields {
            leadingComment := field.Comments.Leading.String()
            trailingComment := field.Comments.Trailing.String()

            line := fmt.Sprintf("%s %s `json:\"%s\"` %s", field.GoName, field.Desc.Kind(), field.Desc.JSONName(), trailingComment)
            // 输出 行首注释
            g.P(leadingComment)
            // 输出 行内容
            g.P(line)
        }
        // 输出 }
        g.P("}")
    }
    g.P() // 换行
}

就是如此简单的10几行代码,就可以读取.proto文件,并生成.go文件了。接下来我们详细的介绍一下里面主要的变量以及对象。

2.4.1 第一步先生成 GeneratedFile 对象
filename := file.GeneratedFilenamePrefix + ".struct.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath) 
2.4.2 第二步生成go代码的package
// 输出 package packageName
g.P("package ", file.GoPackageName)
g.P() // 换行
2.4.3 第三步便利proto文件的message

因为我们这里是找message然后生成struct,所以使用file.Messages来获取所有的message,然后遍历

    for _, m := range file.Messages {
        // 输出 type m.GoIdent struct {
        g.P("type ", m.GoIdent, " struct {")
        for _, field := range m.Fields {
            leadingComment := field.Comments.Leading.String()
            trailingComment := field.Comments.Trailing.String()

            line := fmt.Sprintf("%s %s `json:\"%s\"` %s", field.GoName, field.Desc.Kind(), field.Desc.JSONName(), trailingComment)
            // 输出 行首注释
            g.P(leadingComment)
            // 输出 行内容
            g.P(line)
        }
        // 输出 }
        g.P("}")
    }

这一步代码主要生产go的struct,生成后的样子如下:

type User struct {
    // xingming
    Name string `json:"name"` // 姓名
    // age
    Age int64 `json:"age"` // 年龄
}

三、编译插件

我们把这个代码,在本地编译安装,在项目根目录执行go install

go install

此时我们到自己的GOPATH目录查看是否生成
我们cd到$GOPATH的bin目录,一般go install安装的命令都在这里

cd  $GOPATH/bin && ls -al


我们看到了 protoc-gen-go-struct 二进制命令。

四、protoc使用protoc-gen-go-struct插件

我们在其他地方写测试方法,写一个 pt.proto文件

mkdir protoc_struct && cd protoc_struct && touch pt.proto

然后把pt.ptoto里面写入代码

syntax = "proto3";
package pt;
option go_package = "./protoc_struct";

message User {
  // xingming
  string name = 1;// 姓名
  // age
  int64 age = 2;// 年龄
}

最后我们在执行 protoc 命令

protoc --go-struct_out=./ pt.proto

此时我们发现生产了pt.struct.go文件
文件里面生成的go代码如下

package protoc_struct

type User struct {
    // xingming
    Name string `json:"name"` // 姓名
    // age
    Age int64 `json:"age"` // 年龄
}

和上面的.proto文件一一对应。
写到这里,我们自己写一个go对应的protobuf插件就完成。

本文参与了思否技术征文,欢迎正在阅读的你也加入。

推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
author-avatar
梅花七
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有