Beego 2.0 初心
模塊化與解耦
AOP 初次嘗試
更好的可觀測性
-
tracing 和 metrics
logging
防呆設計 ORM
姍姍來遲的配置模塊優化
adapter 模塊和升級指南
未來
Beego 2.0 初心
在鴿子精的本性屢次發作之後,我們終於官宣Beego v2.0要和大家見面了。
在得知我們開啓了 Beego 2.0 的時候,很多人問我們,你們搞 2.0 幹啥呀?
其實不論是 Beego 1.x 還是 Beego 2.0,我們的初心一直沒有改變,也就是希望能夠爲 Go 企業級應用開發提供一種一站式的解決方案,尤其是我們希望能夠爲中小型企業賦能,幫助這些企業提升研發效率、工程質量,以快速推出新產品,快速完成迭代。
我們很多大型企業都有很多規範,但是我們中小企業在規範和基礎組件上面經常是很混亂,開發早期都是怎麼快怎麼來,但是當有新同學加入或者有一個同學離職,就會導致相同功能不同的寫法、引入同一功能的不同組件(例如日誌引入logrus、zap), 最終會導致維護困難、重構難,遇到問題不知道怎麼查找問題點,這些都是我們很多企業遇到的困難。
而站在我們打工人的角度,我們也相信,真正的勞動者,不應該是被禁錮在流水線上的囚犯。技術應當解放生產力,將勞動者者從繁瑣枯燥的重複性勞動中解放出來,去從事具有創造性藝術性的活動。
所以,我們的設計目標,就是做一個“簡單、高效、一站式”的公司統一應用框架。具體而言則是圍繞這六個特徵進行:
統一標準:讓不同層次的工程師可以基於同樣的標準寫代碼;
低代碼化:自動生成重複代碼,從 CRUD 中解脫出來,專注業務;
錯誤收斂:設計錯誤處理標準,大幅提升錯誤發現、排查和修復的效率;
可治理性:管理應用的生命週期,讓應用能夠在開發、測試、部署和運維階段都更簡單方便;
可插拔性:自由選擇使用的組件,自由接入自己的組件;
可觀測性:全方位觀測應用,掌握應用的各個方面;
那麼,和原先的 Beego 比起來有什麼不同呢?讓我們一起來看看 Beego 2.0 的幾個重大變化吧!
模塊化與解耦
在準備着手 v2.0 的時候,我們做了一些調查。一方面是在社區發放調查問卷,一方面是整理 Beego 的 issue,最終我們確認了 v2.0 的總體思路,那就是:模塊化,模塊化,模塊化!
其實在 v1.x 的時候,模塊化已經有了堅實的基礎:
如圖所示,很顯然 Beego 是由這八個模塊組成。而 Beego 被人詬病太重,也是因爲 Beego 一個項目就具有這麼多模塊。而實際上,各個模塊即便在 1.x 裏面也是相當獨立的,只有小部分會耦合在一起。
因此我們要做的是就是重新組織一下這些模塊,並且將模塊之間的耦合降到最低。
這方面我們參考了幾個非常優秀的開源框架,重新劃分了我們的模塊組織:
正如上圖所示,我們希望模塊之間只依賴於底層的模塊,而互相之間不會依賴。
對於一個企業來說,如果選擇 Beego,那麼它很容易就統一所有的技術選型(統一標準,擴展組件),並且帶來極高的研發效能——這對大多數的中小型企業、初創企業來說至關重要。
AOP 初次嘗試
AOP 是一個企業級應用離不開的場景。例如我們想要支持的更好的可觀測性,那麼在接入諸如 opentracing
, prometheus
的時候,就需要這種無侵入式的 AOP 解決方案。
我們思考過幾個方案,比如說編譯器接口,在編譯階段通過修改 AST 注入特定的 AOP 邏輯;又或者採用代理,但是這需要一個完整的依賴注入框架支持,從開發體驗上來講,也並不是特別好;
從理論上來說,我們喜歡第一種方案,即編譯器接口的方案。可惜 Go 並未暴露這種編譯器的接口,允許我們在編譯過程中修改代碼。
在無法使用這些手段的約束下,我們只能折中設計了一個能力有限的無侵入式的 AOP 方案。
那就是使用 filter-chain
的設計模式來完成這一類的 AOP 需求。
具體做法是,我們爲主要模塊設計了 filter-chain
。這些接口大抵類似,只是參數略有不同。例如在 httplib 中,其接口是:
type FilterChain func(next Filter) Filter
type Filter func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error)
用戶很容易可以通過擴展 Filter
接口來完成自定義的功能,例如日誌:
func myFilter(next httplib.Filter) httplib.Filter {
return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
r := req.GetRequest()
logs.Info("hello, here is the filter: ", r.URL)
// 別忘了調用下一個filter
return next(ctx, req)
}
}
這個例子只是記錄了一下請求 URL。實際上,如果你要實現熔斷限流和容錯處理,都可以藉助這些 Filter
來完成。
目前,總的來說,AOP 是一個初次嘗試的東西。當前的這種機制還不是特別通用。它被侷限在一個模塊暴露的公共 API 部分,也就是模塊入口和出口部分。而且它也不是聲明式的。我們所期望的,理想中的 AOP 應當是聲明式的,更加貼近 Java Spring 那種聲明式 AOP 框架。
更好的可觀測性
在 Beego 1.x 中,可觀測性三個方面, tacing
, metrics
和 logging
,其中 logging
已經得到了很好的支持。
tracing 和 metrics
我們的目標是支持 opentracing
和 prometheus
。選擇 opentracing
,是因爲它頗有一種業界標準的感覺。大多數有影響力的 tacing 框架,都會考慮提供 opentracing
API 的支持。這意味着,用戶可以自行選擇他們青睞的 tracing 框架,只需要在啓動的時候注入特定的實現即可。
promethues
雖然不具備 opentracing
API 那種普適性。但是它已經是業界事實上的標準了。
還有一個候選者,就是 opentelemetry
。雖然它號稱是下一代的 tracing
標準,但是目前來說,它還不具備 opentracing
那麼大的影響力。它自身的實現也很粗糙,因此排除了它。
我們的重點目標是,讓使用 Beego 的應用可以實現無縫連接。
例如,假如我用 Beego 同時有 A 和 B 兩個應用,而且 A 會調用 B,那麼我只需要開啓了 tracing
功能,那麼我在 A 上使用 httplib 調用 B 的 http 接口,就能實現端到端的鏈路監控。
而實際上,我們也達到了這個目標。例如我測試時候一個效果圖,這裏我選擇了 zipkin
作爲 opentracing
的實現:
logging
logging
方面,這一次我們也加進去了一個呼聲很高的特性——自定義日誌格式。自定義日期格式是一個很實用的功能,一方面是公司可能有日誌格式方面的要求,一方面則是更好支持日誌分析的需求。當然,品位,或者說個人偏好也一併滿足了。
爲了降低大家編寫自定義日誌格式支持的難度,我們額外提供了一個實現 PatternLogFormatter
。例如我們傳入一個期望的日誌格式 %F:%n|%w%t>> %m
那麼最終輸出的日誌如:
/User/go/beego/main.go:10|2020-12-11[E]>> message
防呆設計 ORM
每次有人問你們爲什麼要設計新的 ORM 的事務接口?我們都是統一回答:因爲用戶頻繁誤用,導致我們回答 issue
不堪其擾。
那麼原來的 ORM 有什麼弊端呢?
原來的 ORM,如果按照文檔來使用,是毫無破綻的。只不過原本的 ORM 是一個有狀態的東西,比如說裏面有一個 isTx
標記,標記創建的 ORM 對象當前是否處於一個事務中。
然後這就會導致,用戶在不同的 goroutine 之中使用同一個 ORM 對象的時候,經常出現併發問題。
這一次我們就改進了這個問題。當然“防呆設計”是一個開玩笑的說法。畢竟一個 API 經常被誤用,本身就說明了它在設計上是不夠直觀的。
我們這一次摒棄了原本維持 isTx
的做法,而是在開啓一個事務的時候,返回一個全新的 TxOrm
。這個對象是一次性的,隨取隨用,用完即丟。
而且,爲了減輕手動管理事務的負擔,我們提供了一個利用閉包管理事務的 API,用起來很方便:
err := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
// data
user := new(User)
user.Name = "test_transaction"
// insert data
// Using txOrm to execute SQL
_, e := txOrm.Insert(user)
// if e != nil the transaction will be rollback
// or it will be committed
return e
})
姍姍來遲的配置模塊優化
這一次我們在配置模塊裏面加入了三個很有意思的方法:
Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error
Sub(key string) (Configer, error)
OnChange(key string, fn func(value string))
說起來這三個方法也只能算是終於把應該有的東西支持了。
不過我們還是想要額外提及一下, Unmarshaler
+ Sub
簡直是神器,能夠讓用戶非常簡單的就按照自己的模塊組織配置,而後將這些配置反序列化爲特定對象。
並且,我們這一次終於迎來第一個遠程配置中心的實現 etcd
。用戶只需要引入 etcd
的實現,就可以使用了!
其它遠程配置中心的支持,歡迎大家來提 PR 啊!
adapter 模塊和升級指南
這一次,因爲項目結構發生了重大變更。爲了減輕升級的負擔,我們特意開發了一個 adapter
模塊。該模塊的結構和原有的結構一樣,只不過所有的內部實現都用 2.x 的 API。
用戶只需要將包名從 v1.x 的包名換成 adapter
的包名就可以了。
這種替換,我們在 bee
裏面也做了支持,只需要執行:
bee fix -t 2
未來
我們的願景是成爲“最簡單易用的企業級應用開發框架!”。
接下來我們的工作也會以前面提到的六個特徵作爲指導,圍繞以下方面進行:
統一標準:着重解決當前業務開發測試難的問題,爲 ORM 等模塊提供便利的測試支持;
錯誤收斂:爲全部組件設計統一的錯誤碼和標準化的錯誤信息;
可治理性:增強現有的治理功能,例如暴露
cache
的統計信息,模塊的錯誤信息。同時支持 Beego 應用集羣維度監控和治理能力;可觀測性:繼續在 Beego 內部加多埋點和日誌信息,並且允許設定參數開啓或者關閉(包括部分開啓和部分關閉)。也希望能夠提供全面的 benchmark 測試,對所有模塊的性能做到心中有數;
技術方向
從技術發展方向而言,我們的三個核心詞彙就是:泛型,容器(DI),雲原生。
泛型會是我們的第一個目標。因爲,每一次泛型出現,都會導致已有的模式發生巨大的變化。例如說現在的 ORM 接口幾乎就是隻接收 interface
對象,基本上無法享受到編譯期的檢查。我們也希望我們能夠在泛型出來之時,能夠迅速搶佔技術高地,對外輸出我們的實踐標準。
容器,包括依賴注入,應用生命週期管理,AOP 方面,會是我們的第二個目標。AOP 這是爲了將用戶從無聊的創建對象,管理對象和銷燬對象中解放出來。而 AOP,前面已經提及了,這是一個做企業級應用開發框架不得不解決的一個問題。
第三個目標雲原生則是,探索如何利用好雲原生的技術,增強 Beego 應用的可用性、穩定性,減輕對 Beego 應用的運維管理負擔。
在此我們先立一個 Flag,希望泛型正式發佈的時候,我們已經做好準備了。
致謝
感謝這些爲 Beego2.0 做出貢獻的人員(Github ID):
flycash, jianzhiyao, AllenX2018, IamCathal, tayoogunbiyi, wangle201210, mlboy, askuy, flutterWang, higker, weirubo, yitea, Cadenguo, unknowon, vinicio, ACrushTest.
還有那些未列出來但是參與我們社區討論,在 issue 裏面給我們提出很多建議的人。
特別感謝 askuy,他可以說是 Beego 2.0 的總架構師,他豐富的經驗,使我們更有底氣,更有自信。
尤其感謝謝大,爲我們把控方向,給予了我們充分的信任,讓我們能夠在 Beego 裏面大展身手。
最後要感謝我們的社區,給了我們很多有用的建議和反饋。以及對我們鴿了好幾次的無限包容(我們還是希望你們多來催催我們不然就更加容易鴿了)。
歡迎大家來使用我們 Beego!我們準備了文檔和例子,還等什麼, 一起來啊!
- 官網: https://beego.me
- 例子 https://github.com/beego/beego-example
- Release Note:https://github.com/beego/beedoc/blob/master/en-US/intro/releases.md