go-zero微服务框架代码生成神器goctl原理分析(一)

简介: go-zero微服务框架代码生成神器goctl原理分析(一)

推荐下go-zero 微服务框架,也是最近很火很有人气的框架,致力于打造国内最简单好用的框架。


火在哪?几分钟搞定个接口和微服务,还不用担心稳定性和高并发,这不香吗?


作者的理念很好,工具大于约定和文档的理念,编码自动化,让精力更多的放在业务和创新上,大幅度的提高效率和生产力,这会是个趋势。


golang圈子不大,微服务框架框架屈指可数:除了go-micro、go-kit,几乎没有其他选择。go-zero为此提供第三个可能。


说起go-zero提高生产力的地方,就不得不说goctl。


goctl是go-zero配套的代码生成器。


简直是神器,只需要定义好服务接口和请求体,应答体,一键自动生成整套框架和代码。

对goctl是如何生成模板框架和代码感觉很好奇,于是就打算对goctl的实现做个分析。或许哪天也想搞个这么个代码生成器,可以参考下go-zero作者的思路和方法,也来提高提高效率,自动生成框架或模板代码多好。


分析之前先来看看goctl是怎么用的吧。


首先得先安装goctl了,


git clone https://github.com/tal-tech/go-zero
cd go-zero/tools/goctl
go build goctl.go


最后生成goctl.exe 复制到Go的安装目录bin下 。


简单的使用方法:


goctl api    go    -api       hello.api   -dir    .


#代码说明如下
goctl  api      go       -api             open.api            -dir                     .
 |      |        |         |                 |                  |                      | 
      生成api  go语言     指定api模板文件   模板文件名称         指定生成代码存放路径     当前文件夹


创建项目


生成go.mod文件


输入如下指令,创建项目


  1. mkdir hello


  1. cd hello


  1. go mod init hello


定义hello.api


type (
  UserOptReq struct {
    mobile string `json:"mobile" form:"mobile"`
    passwd string `json:"passwd" form:"passwd"`
    code   string `json:"code" form:"code"`
  }
  UserOptResp struct {
    id    uint   `json:"id"`
    token string `json:"token"`
  }
  //图片验证码支持
  VerifyReq struct {
    ticket string `json:"ticket" form:"ticket"`
  }
  //图片验证码支持
  VerifyResp struct {
    data string `json:"data"`
  }
)
service open-api {
  @doc(
        summary: 公开的api函数
        desc: >
        register 用户注册,
        authorization 用户登录,
        verify 图片验证码接口
    )
  @server(
    handler: registerHandler
    folder: open
  )
  post /open/register(UserOptReq) returns(UserOptResp)
  @server(
    handler: authorizationHandler
    folder: open
  )
  post /open/authorization(UserOptReq) returns(UserOptResp)
  @server(
    handler: verifyHandler
    folder: open
  )
  post /open/verify(VerifyReq) returns(VerifyResp)
}


生成代码


采用如下指令生成代码:


goctl api    go    -api       hello.api   -dir    .




运行一下


go run open.go


测试一下:


curl http://127.0.0.1:8888/open/register -X POST -H "Content-Type: application/json" -d {\"mobile\":\"15367151352\",\"passwd\":\"testpwd\",\"code\":\"asdf\"}
{"id":0,"token":""}


熟练的话几分钟时间就搞定一个接口的大致框架剩下就剩添加业务了,这效率,还能更快吗?简单的接口分钟搞定。


接下来揭开goctl的神秘面纱,看下是如何实现这么强大的功能的,从中汲取点儿营养。跟着上面的指令流程,goctl api    go    -api       hello.api   -dir    .走一遍吧,看看经历了什么。


打开源码目录里,进入github.com\tal-tech\go-zero\tools\goctl,


打开goctl.go,这里应该是应用的入口了。goctl.go这个文件的源码不多,使用了一个外部库github.com/urfave/cli


先来看下这个cli库,这个库使用还是挺简单的,用来生成命令行应用的神器。


简单的命令行,直接用go内置的flag包实现了命令行参数的解析就可以了。但若是造成一个工具,得方便使用吧,有一些命令行的使用说明吧,使用urfave/cli这个库就简单了。


一个简单示例:


package main
import (
  "fmt"
  "github.com/urfave/cli"
  "os"
)
var (
  Version = "1.0.0"
)
type Commands struct {
}
func (this *Commands) Test(cli *cli.Context) {
  fmt.Println("this is Test cmd proc")
  uid := cli.Int("uid")
  username := cli.String("name")
  fmt.Println(uid, username)
}
//模拟子命令
func (this *Commands) Test1(cli *cli.Context) {
  fmt.Println("this is Test1 cmd proc")
  u := cli.Int("u")
  n := cli.String("n")
  fmt.Println(u, n)
}
func main() {
  app := cli.NewApp()
  app.Usage = "this is a cli tool to do someting"
  app.Version = Version
  app.Commands = []cli.Command{
    {
      Name:   "test",
      Usage:  "this is a test cmd,for example:test -uid=x -name=y",
      Action: (&Commands{}).Test,
      Flags: []cli.Flag{
        cli.IntFlag{Name: "uid,ud", Usage: "this is a flag,-uid"},
        cli.StringFlag{Name: "name,ne", Usage: "this is a flag,-name"},
      },
      //以下子命令,根据需要有,非必须
      Subcommands: []cli.Command{
        {
          Name:   "test1",
          Usage:  "this is a sub cmd test1",
          Action: (&Commands{}).Test1,
          Flags: []cli.Flag{
            cli.IntFlag{Name: "u", Usage: "this is a flag,n"},
            cli.StringFlag{Name: "n", Usage: "this is a flag,usage:-n"},
          },
        },
      },
    },
  }
  err := app.Run(os.Args)
  if err != nil {
    fmt.Print("command error :" + err.Error())
  }
}





或者这样,注意参数输入可以为-u,或者--u都行,或者-u 后面跟等号或者省略=号直接写内容都可以。



了解完cli内容,那么可知上面的goctl命令的执行顺序了,goctl api    go    -api       hello.api   -dir    .


就是执行了api命令的子命令go的那项,参数输入是-api,内容为hello.api,另一项参数输入为-dir,内容为当前目录。


Action: gogen.GoCommand,


接下来就进入gogen.GoCommand里面看看吧,代码在github.com\tal-tech\go-zero\tools\goctl\api\gogen的gen.go里面。


const tmpFile = "%s-%d"
var tmpDir = path.Join(os.TempDir(), "goctl")
func GoCommand(c *cli.Context) error {
  apiFile := c.String("api")
  dir := c.String("dir")
  if len(apiFile) == 0 {
    return errors.New("missing -api")
  }
  if len(dir) == 0 {
    return errors.New("missing -dir")
  }
  p, err := parser.NewParser(apiFile)
  if err != nil {
    return err
  }
  api, err := p.Parse()
  if err != nil {
    return err
  }
  logx.Must(util.MkdirIfNotExist(dir))
  logx.Must(genEtc(dir, api))
  logx.Must(genConfig(dir))
  logx.Must(genMain(dir, api))
  logx.Must(genServiceContext(dir, api))
  logx.Must(genTypes(dir, api))
  logx.Must(genHandlers(dir, api))
  logx.Must(genRoutes(dir, api))
  logx.Must(genLogic(dir, api))
  // it does not work
  format(dir)
  createGoModFileIfNeed(dir)
  if err := backupAndSweep(apiFile); err != nil {
    return err
  }
  if err = apiformat.ApiFormat(apiFile, false); err != nil {
    return err
  }
  fmt.Println(aurora.Green("Done."))
  return nil
}


从这段代码里,可以看到,从命令行取出了hello.api这个文件,先经过parser.NewParser(apiFile)这一处关键处理,这应是goctl的神秘地方之一,后面会单独分析。


然后依次是如果目录不存在,创建目录,logx.Must用意就是must里面的必须执行成功,不成功的话就中断退出了,并输出日志。


接下来流程依次是


genEtc生成etc配置文件目录和内容。


genConfig-> genMain,genServiceContext,genTypes,genHandlers,genRoutes,genLogic


然后执行go fmt指令格式化一下代码format(dir)


func format(dir string) {
  cmd := exec.Command("go", "fmt", "./"+dir+"...")
  _, err := cmd.CombinedOutput()
  if err != nil {
    fmt.Println(err.Error())
  }
}


createGoModFileIfNeed 创建下go.mod如果需要的话。


backupAndSweep,备份和清理处理。


最后是apiformat.ApiFormat(apiFile, false)是干嘛的,先记下来,后面再看。既然是分析原理嘛,起初没必要每个点都扣那么细,讲究方法,先整体后局部,先易后难。


以上仅仅是冰山一角,一个大致的流程。


难点和精华应该是那个parser.NewParser()处理和后面genEtc和genMain等里面的text/template模板解析和作者实现的获取注解的方法实现上。


下面接着分析parser,这块儿是重点,涉及go/parser和go/ast库的使用。


相关文章
|
4月前
|
监控 算法 NoSQL
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
🌟蒋星熠Jaxonic:Go微服务限流熔断实践者。分享基于滑动窗口、令牌桶与自适应阈值的智能防护体系,助力高并发系统稳定运行。
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
|
9月前
|
人工智能 数据可视化 编译器
Go interface实现分析
本文深入探讨了Go语言中接口的定义、实现及性能影响。接口作为一种“约定”,包含方法签名集合,无需依赖具体类型即可调用方法,隐藏了内部实现细节。文章分析了接口的两种实现方式(iface和eface)、按值与按指针实现的区别,以及nil接口与普通nil的区别。同时,通过反汇编代码对比了接口动态调用与类型直接调用的性能差异,指出接口调用存在内存逃逸和无法内联的问题。最后总结了接口的优势与局限性,强调在实际开发中需根据场景合理选择是否使用接口。
241 13
|
5月前
|
监控 前端开发 数据可视化
Github 12.3kstar, 3分钟起步做中后台?Go+Vue 脚手架,把权限、代码生成、RBAC 都封装好了
Go-admin 是基于 Gin + Vue 的中后台脚手架,集成 Casbin RBAC 权限、JWT 鉴权、GORM 数据库操作与 Swagger 文档,内置用户、角色、菜单等管理模块。提供代码生成器与表单构建器,支持多租户与多前端框架(Element UI/Arco/Ant Design),3 分钟快速搭建企业级后台,助力高效交付。
461 4
|
7月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
283 24
|
7月前
|
存储 人工智能 安全
深入理解 go sync.Map - 基本原理
本文介绍了 Go 语言中 `map` 在并发使用时的常见问题及其解决方案,重点对比了 `sync.Mutex`、`sync.RWMutex` 和 `sync.Map` 的性能差异及适用场景。文章指出,普通 `map` 不支持并发读写,容易引发错误;而 `sync.Map` 通过原子操作和优化设计,在某些场景下能显著提升性能。同时详细讲解了 `sync.Map` 的基本用法及其适合的应用环境,如读多写少或不同 goroutine 操作不同键的场景。
321 1
|
8月前
|
算法 Java Go
Go内存原理-GC原理
本文介绍了Go语言中垃圾回收(GC)机制的发展与实现原理,涵盖从标记-清除算法到三色标记法,再到三色标记加混合写屏障的演进过程,重点解析各版本GC的核心思想、优缺点及性能优化方向。
233 4
|
9月前
|
安全 Go 开发者
Go语言之切片的原理与用法 - 《Go语言实战指南》
切片(slice)是Go语言中用于处理变长数据集合的核心结构,基于数组的轻量级抽象,具有灵活高效的特点。切片本质是一个三元组:指向底层数组的指针、长度(len)和容量(cap)。本文详细介绍了切片的声明与初始化方式、基本操作(如访问、修改、遍历)、长度与容量的区别、自动扩容机制、共享与副本处理、引用类型特性以及常见陷阱。通过理解切片的底层原理,开发者可以更高效地使用这一数据结构,优化代码性能。
331 13
|
9月前
|
Go 调度
GO语言函数的内部运行机制分析
以上就是Go语言中函数的内部运行机制的概述,展示了函数在Go语言编程中如何发挥作用,以及Go如何使用简洁高效的设计,使得代码更简单,更有逻辑性,更易于理解和维护。尽管这些内容深入了一些底层的概念,但我希望通过这种方式,将这些理论知识更生动、更形象地带给你,让你在理解的同时找到编程的乐趣。
185 5
|
9月前
|
人工智能 数据可视化 JavaScript
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
Juggle是国内首个开源的微服务编排框架,专注于解决企业微服务进程中接口重复开发、系统对接复杂等问题。它提供零代码、低代码和AI增强功能,通过可视化拖拽快速组装简单API为复杂接口,支持多协议、多语言脚本和流程多版本管理。相比国外框架如Conductor,Juggle更贴合国内需求,具备高效开发、企业级可靠性及信创适配等优势,助力企业实现敏捷创新与数字化转型。
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
|
10月前
|
存储 监控 算法
员工行为监控软件中的 Go 语言哈希表算法:理论、实现与分析
当代企业管理体系中,员工行为监控软件已逐步成为维护企业信息安全、提升工作效能的关键工具。这类软件能够实时记录员工操作行为,为企业管理者提供数据驱动的决策依据。其核心支撑技术在于数据结构与算法的精妙运用。本文聚焦于 Go 语言中的哈希表算法,深入探究其在员工行为监控软件中的应用逻辑与实现机制。
252 14

热门文章

最新文章