自動化生成代碼的祕密

我作過兩個自動化生成代碼的項目,scaffoldredis-orm
scaffold 主要是經過數據庫表定義來生成基於表的增刪改查的基礎管理工做;
redis-orm 是經過yaml的結構定義文件生成關係型數據庫與redis的常規操做實現。
公司裏還有一套微服務的自動化生成框架,可以快速的經過protobuf的定義文件生成項目的框架代碼。c++

自動化生成代碼有個最大的優勢:減小程式化的編碼。所謂程式化的編碼就是,一般這些編碼的工做量會隨着業務量的增加線性增加,同時又是最沒有技術含量的工做。因此經過開發自動化生成工具很是有必要,減小無謂的工做量同時大大提高工做效率,把你們解放出來作更有意義的事。git

不管是我寫的自動化生成工具或是公司的微服務框架生成工具仍是其它一些官方工具,都有一個共同原理,所謂自動化生成代碼的祕密,即程序員

經過結構化的元數據生成模式代碼github

這句話中有兩個關鍵詞:golang

  • 結構化的元數據
    結構化的元數據的來源能夠是:redis

    • 數據表定義
      例子: scaffold
    • 結構化的配置文件(yaml, toml 等等)
      例子: redis-orm
    • 服務接口定義(Thrift, ProtoBuffer等等)
      例子:grpcmicro
    • 程序代碼中的類型、對象、接口等等
      例子:stringermock
  • 模式代碼
    模式代碼,即全部生成的代碼是符合必定規律的,而這種規律就是基於元數據而言的。shell

最簡單的例子數據庫

官方的工具stringer就是一個自動化生成代碼工具,主要用途是經過枚舉值的變量名生成String函數接口,經常使用場景就是在定義程序狀態碼中使用。其中,結構化的元數據就是枚舉類型的定義。編程

package codes

type Code uint32

//go:generate stringer -type=Code

const (
  OK Code = 0
  Canceled Code = 1
  Unknown Code = 2
  InvalidArgument Code = 3複製代碼

這是一個簡化版的GRPC狀態碼的例子,在文件所屬目錄下經過如下stringer命令便可生成代碼文件code_string.gobash

$: stringer -type Code複製代碼

生成的代碼以下:

// Code generated by "stringer -type Code"; DO NOT EDIT

package codes

import "fmt"

const _Code_name = "OKCanceledUnknownInvalidArgument"

var _Code_index = [...]uint8{0, 2, 10, 17, 32}

func (i Code) String() string {
    if i >= Code(len(_Code_index)-1) {
        return fmt.Sprintf("Code(%d)", i)
    }
    return _Code_name[_Code_index[i]:_Code_index[i+1]]
}複製代碼

原代碼函數有一句註釋的語句:

//go:generate stringer -type=Code複製代碼

經過該語句,能夠在命令行中執行以下命令,效果相同:

$: go generate複製代碼

一個小技巧,在製做自動化生成代碼工具的過程當中有時候會頗有用。

微服務框架的自動化

微服務如今很火,如何開發一個微服務框架的自動化生成工具呢?

首先,咱們要清楚什麼是框架

框架是對接口的抽象

這是我我的對框架的總結,經過將項目中變化的部分經過接口抽象出來,提供給開發者,將不變的或者配置可變的放入框架中。

其實,grpc 已是一個簡單的微服務框架了,只是功能比較單一,僅僅經過protobuf的定義生成客戶端與服務端代碼框架。它是怎麼作到的?

管道的概念,作服務端的人都很是熟悉。能夠用管道的概念類比一下grpc框架代碼的生成過程

protoc | protoc-gen-go | plugin:grpc

protoc編譯器經過讀取protobuf協議與接口配置,輸出結構化元數據給 protoc-gen-go,由它生成 go 代碼,在protoc-gen-go中會用到 plugin:grpc 的插件實現grpc框架代碼的定製生成。

固然, protoc-gen-go 調用 plugin:grpc 不是經過管道的方式。

要實現微服務框架的自動化的關鍵全在 plugin:grpc 中了。由於 plugin:grpc 就是一個代碼生成器。你想要的全部心裏戲所有能夠在這裏實現。包括:

  • 服務發現
  • 上下文定製
  • 錯誤處理
  • 日誌
  • 統計

所有能夠在框架裏實現,僅僅暴露簡單的接口供開發人員開發。

爲了讓生成代碼更加精煉、可讀性更強,共用的一些函數都會經過公用包的形式實現。

在安裝GRPC的過程當中,有這樣一條安裝命令:

$: go get -u github.com/golang/protobuf/{proto,protoc-gen-go}複製代碼

其中,包proto就是protoc生成go代碼提供的公用包

結構化元數據

有時候閱讀代碼能夠幫助咱們理解protobuf協議。在公司的微服務框架裏用到了custom option. 在官方文檔說,這個屬性對於大部分開發者都是不會用到的。由於這個屬性僅有在須要開發本身的框架代碼時纔會使用到。編寫模式代碼中,能夠經過custom option控制框架代碼的生成。

模式代碼

除告終構化的元數據,模式代碼的質量直接影響了項目自己的質量。模式代碼保持精煉,可讀性強都是一些基本要求。不貼代碼了,具體代碼參見grpc.go.

如何編寫本身的Plugin,除了參考GRPC自己的Plugin實現,還能夠參考這個項目
micro/protobuf.

自動化生成代碼常見的坑

在開發自動化生成代碼工具的過程當中,關鍵一步是編寫模式代碼。一般模式代碼必定是經過不斷的迭代才能達到所謂的完美。因此,在不斷迭代的過程當中,就會出現,很痛苦的,改變接口

若是隻是生成的代碼改變接口可能影響面還比較小,只須要相應的修改調用方代碼便可。可是若是生成代碼中調用的公用包接口發生改變了,可能之前生成的代碼就會發生故障。這也是我真實碰到過的一個坑。爲了防止相似錯誤,能夠經過版本控制的辦法解決。

經過對倉庫打tag,利用gopkg.io實現版本控制,是很是快捷且高效的解決辦法.

如何用好自動化代碼生成工具

用好自動化代碼生成工具的關鍵,除了對生成代碼自己要很熟悉外,還須要瞭解生成工具編寫的模式代碼。瞭解自動化代碼生成工具的原理是很是必要的。

其實框架越強大,對於業務而言越有利,但對喜歡偷懶的程序員而言是不利的。因此利用偷懶來的時間,閱讀框架代碼很是必要。

歸根結底,自動化編程是一項泛化編程技術,之前在c++中是件高端而隱祕的事,將程序執行期的代碼移至編譯期生成。現在,在go語言中,能夠經過模板包template光明正大的幹這件事了。

以上,就是我在開發和使用自動化代碼生成工具中學到的些許經驗,全當拋磚引玉,歡迎指教。

相關文章
相關標籤/搜索