[goa]golang微服务框架学习(二)-- 代码自动生成

之前用过go语言的反射来做一些代码生成,参考这篇

但是这种方式,入侵太强,需要执行对应的申明调用, 所以对GOA框架的自动生成非常感兴趣,于是仔细研究了一下,发现用的比较巧妙, 这里先卖个关子,先看看生成的代码目录结构。

这里使用adder的desgin文件来生成:

package design

import (
        . "github.com/goadesign/goa/design"
        . "github.com/goadesign/goa/design/apidsl"
)

var _ = API("adder", func() {
        Title("The adder API")
        Description("A teaser for goa")
        Host("localhost:8080")
        Scheme("http")
})

var _ = Resource("operands", func() {
        Action("add", func() {
                Routing(GET("add/:left/:right"))
                Description("add returns the sum of the left and right parameters in the response body")
                Params(func() {
                        Param("left", Integer, "Left operand")
                        Param("right", Integer, "Right operand")
                })
                Response(OK, "text/plain")
        })

})

然后生成对应的目录结构如下(如果不知道怎么生成,参考第一篇):

[email protected]:~/gocode/src/goa-adder $tree
.
├── app
│   ├── contexts.go
│   ├── controllers.go
│   ├── hrefs.go
│   ├── media_types.go
│   ├── test
│   │   └── operands.go
│   └── user_types.go
├── client
│   ├── adder-cli
│   │   ├── commands.go
│   │   └── main.go
│   ├── client.go
│   ├── datatypes.go
│   └── operands.go
├── design
│   └── design.go
├── main.go
├── operands.go
└── swagger
    ├── swagger.json
    └── swagger.yaml
  • APP目录,生成的框架相关代码,包含HTTP的路由
  • client目录,生成是go原生请求server的client测试程序,方便测试
  • swagger目录, 生成的swagger文件,可以用swagger来进行API的描述,这样不用自己写API接口文档了(cool)
  • 然后是main.go  , 程序的主入口
  • operands.go 业务逻辑代码,你需要在这里进行修改
//operands.go

package main

import (
    "github.com/goadesign/goa"
    "goa-adder/app"
)

// OperandsController implements the operands resource.
type OperandsController struct {
    *goa.Controller
}

// NewOperandsController creates a operands controller.
func NewOperandsController(service *goa.Service) *OperandsController {
    return &OperandsController{Controller: service.NewController("OperandsController")}
}

// Add runs the add action.
func (c *OperandsController) Add(ctx *app.AddOperandsContext) error {
    // TBD: implement   在这里写对应的函数逻辑
    return nil
}

非常棒,不用再重复写框架低层那些代码了(路由、编解码等等)。

虽然之前也用过前公司的框架(那个是利用java的反射自动生成代码),但遇到自动生成代码这事儿,还是止不住兴奋。

这里先不研究生成的框架代码,先研究一下利用go语言是如何自动生成的吧。

一般自动生成可以分三个步骤:

1)通过自描述语言来定义服务和接口(IDL,DSL都OK)

2)解析描述语言,获取元数据(服务名称,接口名称,接口参数神马的)

3)根据元数据,以及框架对应的模板,生成重复的代码部分

我们来看GOA怎么做的,在goagen中加上 --debug 选项,可以保留中间文件。

//使用命令
goagen --debug bootstrap -d goa-adder/design

//生成目录
[email protected]:~/gocode/src/goa-adder $tree -L 1
.
├── app
├── client
├── design
├── goagen009966755
├── goagen174102868
├── goagen511141286
├── goagen585483469
├── main.go
├── operands.go
└── swagger
├── goagen009966755
│   ├── goagen
│   └── main.go
├── goagen174102868
│   ├── goagen
│   └── main.go
├── goagen511141286
│   ├── goagen
│   └── main.go
├── goagen585483469
│   ├── goagen
│   └── main.go

我们看到,多出几个目录来,而且每个目录,都包含一个main.go和生成的可执行程序,我们随便进入一个目录看看:

//************************************************************************//
// Code Generator
//
// Generated with goagen v0.0.1, command line:
// $ goagen
// --debug bootstrap -d goa-adder/design
//
// The content of this file is auto-generated, DO NOT MODIFY
//************************************************************************//

package main

import (
    "github.com/goadesign/goa/goagen/gen_main"
    "fmt"
    "strings"
    "github.com/goadesign/goa/dslengine"
    _ "goa-adder/design"
)

func main() {
    // Check if there were errors while running the first DSL pass
    dslengine.FailOnError(dslengine.Errors)

    // Now run the secondary DSLs
    dslengine.FailOnError(dslengine.Run())

    files, err := genmain.Generate()
    dslengine.FailOnError(err)

    // We‘re done
    fmt.Println(strings.Join(files, "\n"))
}

然后看出一些端倪,它先把我们design目录整个包含进来,然后调用引擎里面的函数,进行代码的生成。

这里再回到我们的DSL语言写的文件 design.go

package design

import (
        . "github.com/goadesign/goa/design"
        . "github.com/goadesign/goa/design/apidsl"
)

var _ = API("adder", func() {
        Title("The adder API")
        Description("A teaser for goa")
        Host("localhost:8080")
        Scheme("http")
})

这里的API,其实就是在调用引擎里预先定义好的函数,在那里定义的呢?看源码

func API(name string, dsl func()) *design.APIDefinition {
    if design.Design.Name != "" {
        dslengine.ReportError("multiple API definitions, only one is allowed")
        return nil
    }
    if !dslengine.IsTopLevelDefinition() {
        dslengine.IncompatibleDSL()
        return nil
    }

    if name == "" {
        dslengine.ReportError("API name cannot be empty")
    }
    design.Design.Name = name
    design.Design.DSLFunc = dsl
    return design.Design
}

API函数的调用,生成了对应的Design实例,然后把元数据(这里是Name 和一个匿名函数) 都保存到内存里面了。

design对象是在程序初始化的时候(源码这里)就定义好了,并把实例注册到生成引擎中去(其实就是把对象实例传过去,方便后续调用)。

后面调用Generate函数来进行代码的自动生成。

大概就是这个意思,确实很巧妙,DSL定义的都是匿名全局变量,全局变量又是对已经定义好的元数据函数的调用(例如:API等),然后通过包引用把DSL文件包含进来,这样元数据都存在对应的实例内存中去了。

然后就可以随便怎么玩了,通过元数据的类型,来生成对应的文件,妙哉!

但是由于要支持各种嵌套、不同类型以及容错等等,所以实现写起来的代码非常多。

不过,我们可以按照这个思路,来实现一个简单的例子:

//main.go

package main

import "fmt"

//定义DSL语言描述的结构体,用于保存DSL里面的数据
type APIDefinition struct {
    // Name of API
    Name string
    // Title of API
    Title string
    // Description of API
    Desc string

    // DSLFunc contains the DSL used to create this definition if any
    DSLFunc func()
}

//实现DSL对应的API,用于实例化
func API(name string, dsl func()) *APIDefinition {
    api := new(APIDefinition)
    api.Name = name
    api.DSLFunc = dsl

    //偷偷赋值
    g_api = api

    return api
}

//对应的Title赋值
func Title(val string) {
    if g_api != nil {
        g_api.Title = val
    }
}

func Description(d string) {
    if g_api != nil {
        g_api.Desc = d
    }
}

//当前design的实例,这里用全局变量示意
var g_api *APIDefinition

//根据内存中的存储数据来进行代码生成
func generateTest() {
    //这里需要执行一下对应的DSLFunc
    g_api.DSLFunc()
    fmt.Println("get Name: ", g_api.Name)
    fmt.Println("get Title: ", g_api.Title)
    fmt.Println("get Desc: ", g_api.Desc)
}

//这里是DSL申明
var _ = API("adder", func() {
    Title("The adder API")
    Description("A teaser for goa")
})

func main() {
    generateTest()
}

最后运行一下执行的结果:

[email protected]:~/gocode/auto-gen $go run main.go
get Name:  adder
get Title:  The adder API
get Desc:  A teaser for goa

我们已经拿到用户在DSL里面定义的数据了(当然,这里DSL描述是直接写到同一个文件里面,省去了合并引入的过程)。

OK,代码的自动生成原理已经知道了,后面就要分析框架整体的架构和代码了。

时间: 2024-10-06 18:21:16

[goa]golang微服务框架学习(二)-- 代码自动生成的相关文章

微服务框架学习收录链接(包括服务搭建中用到mybatis-plus等)

1.基于Spring Boot和Spring Cloud实现微服务架构学习(一)-Spring框架介绍 https://blog.csdn.net/zeb_perfect/article/details/51945350 2.Spring Cloud生态圈简介 https://blog.csdn.net/rickiyeat/article/details/59172258 3.标题:Spring Boot 快速搭建微服务框架详细教程 http://www.jb51.net/article/123

【GoLang】golang 微服务框架 介绍

原文如下: rpcx是一个类似阿里巴巴 Dubbo 和微博 Motan 的分布式的RPC服务框架,基于Golang net/rpc实现. 谈起分布式的RPC框架,比较出名的是阿里巴巴的dubbo,包括由当当网维护的dubbox. 不知道dubbo在阿里的内部竞争中败给了HSF,还是阿里有意将其闭源了,官方的代码使用的spring还停留在2.5.6.SEC03的版本,dubbox的spring也只升级到3.2.9.RELEASE. 不管怎样,dubbo还是在电商企业得到广泛的应用,京东也有部分在使

golang 微服务以及相关web框架

golang 中国gocn golang Applicable to all database connection pools xorm是一个简单而强大的Go语言ORM库,通过它可以使数据库操作非常简便--文档 Lightweight and fast SQL builder for Go and XORM Go 语言包管理 A web based database admin tool written by Golang support mysql/postgres/sqlite Go 语言

【GoLang】GoLang 微服务、开源库等参考资料

参考资料: GoLang书籍: https://github.com/dariubs/GoBooksGo名库: https://github.com/Unknwon/go-rock-libraries-showcasesGoLang中国: http://golangtc.com/Spring Boot实现微服务: http://blog.netgloo.com/2014/10/06/spring-boot-data-access-with-jpa-hibernate-and-mysql/Spri

【GoLang】go 微服务框架 && Web框架学习资料

参考资料: 通过beego快速创建一个Restful风格API项目及API文档自动化:  http://www.cnblogs.com/huligong1234/p/4707282.html Go 语言构建 RESTful Web 服务:  https://www.oschina.net/translate/hardcore-google-communicating-go Golang中使用 JWT认证来 保障Restful JSON API的安全(英文):   http://www.tuico

go微服务框架go-micro深度学习(一) 整体架构介绍

产品嘴里的一个小项目,从立项到开发上线,随着时间和需求的不断激增,会越来越复杂,变成一个大项目,如果前期项目架构没设计的不好,代码会越来越臃肿,难以维护,后期的每次产品迭代上线都会牵一发而动全身.项目微服务化,松耦合模块间的关系,是一个很好的选择,随然增加了维护成本,但是还是很值得的. 微服务化项目除了稳定性我个人还比较关心的几个问题: 一: 服务间数据传输的效率和安全性. 二: 服务的动态扩充,也就是服务的注册和发现,服务集群化. 三: 微服务功能的可订制化,因为并不是所有的功能都会很符合你的

go微服务框架go-micro深度学习-目录

go微服务框架go-micro深度学习(一) 整体架构介绍 go微服务框架go-micro深度学习(二) 入门例子 go微服务框架go-micro深度学习(三) Registry服务的注册和发现 go微服务框架go-micro深度学习(四) rpc方法调用过程详解 go微服务框架go-micro深度学习(五) stream 调用过程详解 代码在github上 原文地址:https://www.cnblogs.com/li-peng/p/10522084.html

微服务框架surging学习之路——序列化

原文:微服务框架surging学习之路--序列化 1.对微服务的理解 之前看到在群里的朋友门都在讨论微服务,看到他们的讨论,我也有了一些自己的理解,所谓微服务就是系统里的每个服务都 可以自由组合.自由组合这个就很厉害了,这样一来,每个服务与服务之间基本的物理 耦合为0,横向扩展整个系统就会非常非常灵活. surging的厉害之处也恰恰是可以做到这些,所以surging 是.net core 里面一个非常不错的微服务框架. 2.surging的序列化方式 2.1 json.Net surging

AG-Admin微服务框架入门

AG-Admin微服务框架入门  @qq群:一群: 837736451  二群 169824183 一 概要介绍 AG-Admin后台地址:https://gitee.com/minull/ace-security AG-Admin前端地址:https://gitee.com/minull/AG-Admin-v2.0 要想玩儿转spring cloud必须进行一大波儿的学习哦,先有个心理准备. AG-Admin基于Spring Cloud微服务化开发平台,具有统一授权.认证微服务云框架.其中包含