[goa]golang微服務架構學習(二)-- 代碼自動產生

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

之前用過go語言的反射來做一些代碼產生,參考這篇。

但是這種方式,入侵太強,需要執行對應的申明調用, 所以對GOA架構的自動產生非常感興趣,於是仔細研究了一下,發現用的比較巧妙, 這裡先賣個關子,先看看產生的程式碼目錄結構。

 

這裡使用adder的desgin檔案來產生:

package designimport (        . "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")        })})

然後產生對應的目錄結構如下(如果不知道怎麼產生,參考第一篇):

qpzhang@qpzhang:~/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.gopackage mainimport (    "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//組建目錄qpzhang@qpzhang:~/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 mainimport (    "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 designimport (        . "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.gopackage mainimport "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()}

最後運行一下執行的結果:

qpzhang@qpzhang:~/gocode/auto-gen $go run main.goget Name:  adderget Title:  The adder APIget Desc:  A teaser for goa

我們已經拿到使用者在DSL裡面定義的資料了(當然,這裡DSL描述是直接寫到同一個檔案裡面,省去了合并引入的過程)。

OK,代碼的自動產生原理已經知道了,後面就要分析架構整體的架構和代碼了。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.