來自 | 才雲 Caicloud(Caicloud2015)
自 2009 年開源以來,Go 作為一種強大、高效、簡潔、易上手的程式語言,在幫助閱讀、除錯和維護大型軟體系統上發揮著越來越重要的作用。而依託其健康生態,Golang 社群也相繼湧現出諸如 beego、gin、chi、go-restful 等知名框架,為 Go 提供額外功能支援。
但選擇過多,反受其亂。面對層出不窮的優秀框架,不同團隊、不同開發者在框架選擇上往往會出現分歧,不同框架之間也彼此壁壘高築,導致業務與框架耦合,開發效率大大降低。
為瞭解決這類問題,才雲 Caicloud 實現了 Golang API 框架 Nirvana,把 API 從對框架的依賴中徹底解放出來。它專為提高生產力和可用性設計,可擴充套件、效能高,旨在成為才雲 Caicloud 所有 Golang 服務的基石,助力業務的高速開發。
近年來,為了滿足業務發展需要,應對日益激烈的市場競爭,大量企業開始使用容器部署雲工作負載。而隨著容器使用量的增長、Kubernetes 成為容器編排的事實標準,在 Kubernetes 上打造新一代容器雲平臺成了企業謀求發展的必由之路。
為了順應時局,技術團隊除了進行思維上的轉變,還要應對團隊和業務方向的調整,對團隊專案和產品完成切割分化。這之中就涉及對 Go 框架變更的抉擇。
兩年前,才雲 Caicloud 團隊在構建基於 Kubernetes 的雲平臺時,在框架上遇到了不少麻煩:當時 Kubernetes 正值發展期,為了快速開發,工程師們往往傾向於選擇自己熟悉的框架。因此,雖然大多數專案用的是 go-restful(Kubernetes 的選型),但用 beego、gin、chi 等框架的工程師也不在少數,這就給業務整合帶來了很多問題:
它借鑒了 Kubernetes 宣告式 API 的設計,巧妙規避了框架對 API 的侵入。同時,為了實現業務和框架的隔離,它定義一個規範,讓 API 按照規範書寫,完全遮蔽了框架對 API 的影響。
一言以蔽之,Nirvana 框架的設計思路始終圍繞工程師們親歷的種種痛點:
在 Nirvana 中,用一套 API Definition 來宣告式地描述業務 API。下麵是一個列出訊息串列 API 的例子:
Definition{
// 這個 API 傳回的是資源陣列,所以使用 List 方法。
Method: def.List,
// Summary 是一個短語,用於描述這個 API 的用途。這個短語在生成檔案和客戶端的時候用於區分 API。
// 這個字串去掉空格後會作為生成客戶端時的函式名,因此請確保這個字串是有意義的。
Summary: "List Messages",
// 詳細描述這個 API 的用途。
Description: "Query a specified number of messages and returns an array",
// 業務函式
Function: message.ListMessages,
// 對應業務函式的引數資訊。用於告知 Nirvana 從請求的那一部分取得資料,然後傳遞給業務函式。
Parameters: []def.Parameter{
{
// 引數來源
Source: def.Query,
// 引數名稱,作為 key 從 Source 裡取值。
// 與業務函式的引數名稱無關。
Name: "count",
// 預設值
Default: 10,
// 引數描述
Description: "Number of messages",
},
},
// 對應業務函式的傳回結果。用於告知 Nirvana 業務函式傳回結果如何放到請求的響應中。
Results: def.DataErrorResults("A list of messages"),
}
type Message struct {
ID int `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
func ListMessages(ctx context.Context, count int) ([]Message, error) {
messages := make([]Message, count)
for i := 0; i < count; i++ {
messages[i].ID = i
messages[i].Title = fmt.Sprintf("Example %d", i)
messages[i].Content = fmt.Sprintf("Content of example %d", i)
}
return messages, nil
}
很顯然,業務函式並不關心自身是如何暴露給外部的,實現方法也和其他內部函式沒有差別(這隻是一個簡單的例子,更多詳細內容請查閱 Nirvana Definition 檔案:https://caicloud.github.io/nirvana/zh-hans/topics/definition.html)。
在這個例子裡,API Definition 描述了完整的 API 結構,包括 RESTful 路徑、請求方法、請求描述、請求引數、請求響應和請求 handler。框架只需要解析 API Definition,就能得到業務邏輯的入口和出口處理方式。對開發者而言,API 的開發過程從“ 命令式路由 + 資料轉換 + 業務邏輯” 變成了“API Definition + 業務邏輯”。框架與業務邏輯之間透過 API Definition 進行橋接。
API Definition 作為宣告式 API,除了讓框架讀取資訊生成路由和對外提供服務,它本身也完整描述了一個 API 的工作方式。因此,還能用它生成 openapi 檔案和客戶端。在接下來的幾個小節中,將詳述 Nirvana 提供的這些能力。
任何一個 API 框架都具備基本的 HTTP Serve 能力( HTTP 介面服務能力)。在 Nirvana 中,HTTP Serve 由 Golang 基礎庫 http 提供。HTTP 請求的路由方式如下圖所示:
-
Filters 是請求過濾器,可以對不符合要求的請求進行提前響應,而不會進入路由匹配過程;
-
Middlewares 是圍繞請求的中介軟體,能夠控制請求的處理行為;
-
Executor 是最終處理請求的執行器,負責透過引數生成器(ParameterGenerators)和結果處理器(DestinationHandlers)將請求註入到業務邏輯之中,並將傳回結果處理後傳回。
在這裡,Filters、Middlewares、ParameterGenerators 和 DestinationHandlers 都是可以被開發者重新定義的。這也意味著開發者可以改變 Nirvana 的原生處理行為,使之符合自身需求。
Nirvana 另一個工作流是將 API Definition 構建到路由中去,並完成整個服務的啟動工作。
在 Nirvana 啟動時,除 API Definition 之外,它還能夠透過外掛往框架裡註入一些功能,包括註冊 Filters、Middlewares、新增其他路由 endpoint 等。目前 Nirvana 已經提供對 metrics、profiling、tracing、log 等常用外掛的支援。
透過以上兩個工作流,以及 Nirvana 中大量的模組方法註冊介面,開發者可以自行擴充套件 API Definition 的能力,讓 API Definition 與業務邏輯更加貼合。API Definition 單向依賴並描述了業務邏輯,並作為框架和業務之間的橋梁,既達到了宣告式 API 的定義形式,也滿足了業務不依賴框架的標的。
無論是路由還是服務構建過程,Nirvana 的實現都不是 hardcode 的。在框架的大部分包裡,你能發現類似以下形式的程式碼結構:
var consumers = map[string]Consumer{
definition.MIMENone: &NoneSerializer{},
definition.MIMEText: NewSimpleSerializer(definition.MIMEText),
definition.MIMEJSON: &JSONSerializer{},
definition.MIMEXML: &XMLSerializer{},
definition.MIMEOctetStream: NewSimpleSerializer(definition.MIMEOctetStream),
definition.MIMEURLEncoded: &URLEncodedConsumer{},
definition.MIMEFormData: &FormDataConsumer{},
}
// RegisterConsumer register a consumer. A consumer must not handle "*/*".
func RegisterConsumer(c Consumer) error {
if c.ContentType() == definition.MIMEAll {
return invalidConsumer.Error(definition.MIMEAll)
}
consumers[c.ContentType()] = c
return nil
}
在這種結構裡,開發者可以自定義每一塊的實現方式,甚至替換框架預設行為。幫助自己最大限度地利用 Nirvana,使它符合業務特定需求。
在服務構建工作流中,提到了外掛機制。下麵介紹三個重要外掛 metrics、profiling和tracing。
Prometheus 作為 CNCF 的開源專案,為我們提供了非常適於描述業務指標的格式:Prometheus Metrics。Nirvana 提供的 metrics 外掛可以在啟動時預設暴露 /metrics 介面。對此,開發者可以將業務指標註冊到 Prometheus 中,透過這個介面讓 Prometheus 採集。也就是說,Nirvana 不僅能幫助開發者瞭解業務的執行狀態,也為開發者定位業務問題提供了強有力的幫助。
Golang 提供了行程級別的 profiling 能力。Nirvana 透過 profiling 外掛直接將這個能力註入到框架中。開發者只需要啟用外掛即可獲得這些除錯和效能測試能力,獲得業務內部執行的狀態資訊。
在 CloudNative 時代,業務通常會以微服務或 Serverless 的形式進行架構。多個服務之間的互動通常是黑盒的,這易導致我們難以定位分散式架構中的服務問題。Tracing 作為解決分散式架構下的呼叫鏈方案,可以在分散式系統出現問題時為開發者提供大量的資訊,幫助開發者排查問題。Nirvana 的 tracing 外掛使用了 open-tracing 的介面規範,並藉助 CNCF 開源專案 jaeger 的能力,為開發者提供一站式 tracing 方案。
API Definition 和 Nirvana 的結合幫助我們完成了服務構建。在服務之外,透過 API Definition 的描述能力,Nirvana 還實現了基於 openapi 的檔案生成和客戶端生成。
Nirvana 的檔案生成基於 openapi 2.0(即 swagger 2.0)規範,從 API Definition 中提取 API 資訊,生成 API 描述檔案 swagger.json。除了生成 API 描述檔案之外,它還能透過 ReDoc 直接提供檔案服務(更多資訊請檢視 https://caicloud.github.io/nirvana/zh-hans/guides/doc.html)。
$ nirvana api --serve=":8081"
只需一條命令,API 檔案即可生成,並透過 8081 埠提供服務。之後我們就能透過網頁檢視檔案了:
Nirvana 的客戶端生成同樣基於 API Definition ——讀取型別資訊,生成客戶端包(目前僅支援生成 Golang 包)。客戶可以直接取用這個包來呼叫服務,整個客戶端的使用方式與本地呼叫一致(更多資訊請檢視 https://caicloud.github.io/nirvana/zh-hans/guides/client.html)。
$ nirvana client
這個命令會在當前目錄下生成一個 client 目錄,在這個目錄內生成 Golang 客戶端程式碼:
type Client struct {
rest *rest.Client
}
// ListMessages returns all messages.
func (c *Client) ListMessages(ctx context.Context, count int) (messages []Message, err error) {
err = c.rest.Request("GET", 200, "/apis/v1/messages").
Query("count", count).
Data(&messages).
Do(ctx)
return
}
如果您想進一步瞭解 Nirvana,可以參考才雲 Caicloud 雲原生 CI/CD 專案 Cyclone(更多資訊請檢視 https://github.com/caicloud/cyclone)。它包含一個 API 元件 Cyclone Server, 基於 Nirvana 對外提供簡單易用的 API。
一如我們對它的期待:讓 API 從對框架的依賴中涅槃重生。
經過一年多的開源,目前 Nirvana 在基礎 API 服務能力上已經經過內部驗證。由於資料傳輸層和轉換層的複雜度被大大降低,才雲 Caicloud 也切物體驗到了這個開源框架帶來的便捷,以及它在加速業務開發上的顯著效果。
-
持續最佳化擴充套件檔案和客戶端生成的能力,降低開發者在這兩塊上的心智負擔;
-
持續最佳化 metrics、profiling、tracing 的能力,並增加新的雲原生能力,讓這些能力成為雲原生應用的標配;
-
框架模組化加強,讓 Nirvana 的每一塊程式碼皆可定製;
-
最佳化框架效能,降低反射對服務的影響;
-
讓 Nirvana 成為 Golang 的 CloudNative & SOA 框架。
Nirvana 正致力於成為讓業務無感知的框架。目前,才雲 Caicloud 正在這條路上踽踽獨行,也真誠地希望將來能有更多開發者願意加入進來,一起構建 Nirvana,一起建設 Golang 社群,一起擁抱開源的勝利。
Nirvana GitHub:https://github.com/caicloud/nirvana
Nirvana 檔案:https://caicloud.github.io/nirvana/zh-hans/
來自 | 才雲 Caicloud(Caicloud2015);內容 | 郭維 社群開發者
已傳送
朋友將在看一看看到
分享你的想法…
分享想法到看一看