本文來自 CSDN 重磅策劃的《2022 年技術年度盤點》欄目。2022 年,智慧技術變革留下了深刻的腳印,各行各業數字化升級催生了更多新需求。過去一年,亦是機遇與挑戰並存的一年。《2022 年技術年度盤點》將圍繞程式語言、開源、雲端運算、人工智慧、架構服務、資料庫、晶片、開發工具等核心技術領域,特邀一線技術專家親臨分享自身的技術實踐,藉此希望能夠為更多的行業從業者帶來一些借鑑與思考,更好地把握技術的未來發展趨勢。
在本篇文章中,來自 Go 開發社區的資深專家柴樹杉老師將圍繞 Go 語言的技術演進歷程,分享對「Go 2」的最新見解,深度剖析 Go 語言在中國的生態發展與企業應用現狀。
最後,如果您有對技術趨勢的真知灼見,或是深度的應用實踐的新見解,歡迎聯繫 CSDN 投稿,聯繫方式:微信(hanbb120,請備註投稿+姓名+公司職位)、郵箱(tumin@csdn.net)。
作者 | 柴樹杉 責編 | 屠敏
回憶 2019 年,正值 CSDN 二十週年時,我曾寫過一篇關於 Go 語言的總結文章——《Go語言十年而立、Go2蓄勢待發》。彼時年底在 Go 2.0 剛啟動時,一場「黑天鵝事件」的突然到來,引發了全民抗疫。
如今時隔三年之後的 2022 年 12 月,疫情全面放開的同時 Go 2.0 也正式落地了。在 2019 年寫前一篇文章的時候,作者尚在武漢,在 2021 年因疫情影響全家搬遷到了杭州,工作環境和工作的方向內容也都發生了巨大變化。因此希望通過這篇文章回顧下這 3 年來在周邊和 Gopher 社區發生的巨大變化。

當我們在談 Go 2 時,究竟指的是什麼?
2012——初見 Go 1.0
Go 2 是相對於 2012 年 Google 發佈的 Go 1.0 的叫法,是代表包含泛型特性的 Go 2.x。在 Go 1.0 發佈時,官方團隊承諾 Go 1.x 將保持源程式碼級別兼容,就是所謂的 Go 1.x 程式碼在後續的 Go 1.x 環境均可以編譯(但是因為各種外部的因素,編譯可能會遇到很多問題,特別是和平臺有關的程式碼)。
2020——期待 Go 2
因為泛型對於任何一個程式語言中都比較大的特性,當時整個社區都預計包含泛型特性的 Go 將會打破 Go 1.x 不兼容的承諾,因此預期大版本號會同時升級。不過後來官方採取了更為穩妥的漸進開發模式,在 Go 1.18 不破壞 Go 1 兼容性的前提下實現了泛型(當然更激進的標準庫革新也沒有發生)。因此大家期待的 Go 2.x 並未出現。
標題中所謂的 Go 2 目前在現實中並不存在!
Go 2 vs Go 1.20
作為 Gopher,作者依然自行將 Go 1.18+的 Go 語言作為 Go 2 稱呼。這無關於 Go 1 的兼容性承諾——而是包含泛型特性的 Go 語言已經不再是 Go 1 所堅持的「Less is More」的 Go 語言、也不再是 Robert Griesemer、Ken Thompson 和 Rob Pike 三位 Go 發明者設計的 Go 語言(泛型主要由 Ian 操刀)!
正如 C++ 雖然幾乎完全兼容 C 語言、但是 C++ 依然是一個新的程式語言,因為 C++ 和 C 語言有著完全不同的程式設計哲學。同樣,Go 1.18 的泛型帶來的程式設計哲學的巨大變化,我希望通過某些形式區分它。

Go 網站變更歷史
Go 網站風格是 Go 語言變更的風向標,每逢重要的時間節點網站必然會同步跟進。到了 2022 年,Go 語言官網從開源以來走過了 13 年,官網也經歷了 4 次大的變化。
2009 年——青澀年華
2009 年,Go 語言剛剛開源時的官網和 Chrome 瀏覽器一樣青澀(首頁的 rsc 還是一個帥小夥):

2012 年——迎接第一個重大版本
2012 年,Go 1 剛剛發佈是官網首次改版(每次改版表示有重大事件要發生):

2019 年——革新前夕
到了 2019 年重啟 Go 2 開發前夕網站再次改版,重新發布了 Logo:

2021 年——迎接「Go2」時代
到了 2021 年 8 月,重啟 Go 1.18 前夕,官網再次迎來徹底翻新,這次網站地址也變了:

Go 1.18 最終帶來了大家期待已久的泛型特性,網站也增加了更多營銷的內容!作者估計以後的 Go 語言將逐步進入運營時代(圍觀 Go 主倉庫變化的同學們其實可以慢慢散了)。

Go 2 泛型終落地
Go 2 的變化當然不僅僅包含泛型這一個特性,其他諸如模糊測試、結構化的文件、工作區、Arena 提案等都是比較大的特性,但是這裡就不詳細展開了。
認識 Go 泛型
其實 Go 2 的開發從 Go 1.10 逐步啟動,到 Go 1.18 泛型落地(具體參考2019 年的總結文章)。如果將 Go 1.18 作為 Go 2 的啟始版本,到目前即將進入開發週期 Go 1.20,語言主要有以下的變化:
Go 1.18 帶來了最大的泛型特性、Fuzz 模糊測試;
Go 1.19 為中國的龍芯 CPU 提供了支持;
還在開發中的 Go 1.20 標準庫將引入 arena 包提供記憶體池特性。
總體來說,Go 2 時代不僅帶來了泛型的變化,同時也提供了基於 arena 包的手動記憶體管理機制(area 是很多語言性能測試的大殺器)。
雖然有點後知後覺(其實我自己還在堅守在Go1時代的Go1.17版本),為了免俗我們也簡單看下 Go 泛型的例子:
func main() {
s := []int{1, 3, 5, 2, 4}
fmt.Println(index(s, 3))
fmt.Println(index(s, 6))
}
func index[E comparable](s []E, v E) int {
for i, vs := range s {
if v == vs {
return i
}
}
return -1
}
其中,index 就是一個泛型函數,可以針對不同類型的 Slice 做 Index 查詢操作。[E comparable]是在編譯階段定義一個可以被比較的 comparable 的類型 E,然後再基於 E 定義切片和要查詢的元素的類型。
在 main 函數調用 index 函數的時候,Go 的泛型編譯器會自動進行類型推導得到泛型函數需要的參數類型資訊。如果是 Go 1.17 之前的版本,我們可以通過空接口實現類似的函數:
func indexGo17(list, value interface{}) int {
sv := reflect.ValueOf(list)
if sv.Kind() != reflect.Slice {
panic(fmt.Sprintf("called with non-slice value of type %T", list))
}
if reflect.ValueOf(value).Kind() != sv.Type().Elem().Kind() {
panic("type(value) != type(list[_])")
}
for i := 0; i < sv.Len(); i++ {
if reflect.DeepEqual(sv.Index(i).Interface(), value) {
return i
}
}
return -1
}
因為是在運行時才能檢查切片和對應值的類型,可能會出現運行時 panic 的情況,而泛型則可以在編譯階段給出 panic 對應的錯誤(運行時同時依賴執行路徑,編譯時則沒有函數執行路徑的問題)。
因此可以說泛型是類似靜態的接口,可以將某些運行時的類型錯誤左移到編譯階段(並且不依賴執行路徑),而其他性能的差別主要是和實現最佳化有關。
泛型可以說是接口在編譯時的形態,有點類似 Rust 等語言中的 trait。同樣 Rust 語言 trait 是編譯時特性,又延伸出了運行時的 dyn trait。如果將運行時和編譯時接口看作是二維座標系的 X 和 Y 軸,那麼 XY 軸的焦點則是 interface{} 空接口,也就是很多語言的 any 類型。
Go 泛型為何用方括弧?
Go 的泛型除了比較弱外(比如內建運算子無法被過載導致很多自定義類型和內建類型程式碼風格分裂),Go 泛型採用方括弧也是大家吐槽的點。
官方為此專門提供了回答解釋這個選擇(https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-contracts.md#why-not-use-the-syntax-like-c_and-java):

簡而言之,這是為了方便寫編譯器的 parser。用過 C++ 的老碼農應該知道老版本的編譯器不能直接寫 std::vector
不過,我更願意相信用中括弧作為泛型語法是 Go 語言早就想好的(和 parser 無關,那只是官樣的文章)。不相信可以查看 Go 的前身 Limbo 的前任 Aelf 的泛型語法(http://doc.cat-v.org/plan_9/2nd_edition/papers/alef/ref):

其中 adt 關鍵字用於定義新的抽象類型(和 Go 語言的 type 類似),然後定義一個 T 類型的 Stack,這裡就是用方括弧。其實方括弧作為泛型參數正在進入主流,Google 新出的 Carbon 語言就是其中之一。

Go 社區的變化
Go 語言目前已經算做是主流語言,社區的各種訊息和資料都非常多。我們無法全部展開,重點講幾個點。
Go 官方的開發者報告
Go 部落格發佈了 2022 年第二季度 Go 開發者調查報告。據介紹,總共有 5752 名開發者參與了本次調查,分享了他們使用 Go 1.18 中新特性的經驗和心得。
主要有以下幾個重大結論:
泛型已被迅速採用。大多數受訪者都知道 Go 1.18 已正式支持泛型,大約四分之一的受訪者表示已經用上了。
Fuzzing(模糊測試)對大多數 Go 開發者來說是新事物。模糊測試還是一個新玩意,需要官方繼續洗腦。
第三方依賴是最重要的安全問題。官方完善了安全問題管理機制。
Error handling(錯誤處理)仍然是一個痛點。

更完整的報告可以參考官方部落格:https://go.dev/blog/survey2022-q2-results
中國貢獻者俱樂部
中國作為全球最火熱的 Go 語言使用者國家,早就有了很多 Go 使用者組織,比如 Go 中國、Go 中文網、GoCN、Go 夜讀等組織。並且從一開始就有很多中國同學給 Go 貢獻程式碼。
其中韋京光早在 2010 年就深度參與 Go 語言開發,將 Go 語言移植到 Windows 系統並實現了 CGO 支持。之後來自中國的 Minux 實現了 iOS 等諸多平臺的移植,並已經很早就加入 Go 語言開發團隊。2019 年前一個文章中提到了來自天津的史斌(benshi001)給 Go 語言編譯器和運行時提交了很多最佳化,到現在國內貢獻者更加壯大。
因此在 2019 年,Baokun Lee、Ben Shi 和 Meng Zhuo 幾位貢獻者決定成立一個貢獻者組織,抱團取暖。據 Ben Shi 介紹,目前字節跳動、騰訊內部都有團隊在為 Go 編譯器和 Runtime 做貢獻,組織內的小夥伴還貢獻了龍芯到支持程式碼。其中很多小夥伴擁有 Go 主倉庫的評審許可權。組織的網站:https://golangcn.org/ 。
這是早期的部分小夥伴列表:

在 2021 年部分小夥伴聚會的照片:

正如組織官網所言:Go 語言中國區貢獻者俱樂部希望能在這股中國的 Go 語言浪潮中盡其所能,通過鼓勵並幫助更多的中國 Gophers 向上遊 Go 社區貢獻程式碼,並在國內的 Go 語言社區中定期分享 Go 工具鏈的內部實現機制,助力國內社區的繁榮發展以及和國內 Gophers 共同學習成長,共同建設國內的 Go 生態,並願意嘗試作為溝通中國 Go 社區和 Go 語言官方的一道橋樑。
Go 支持國產龍芯架構
龍芯的支持程式碼在 Go 1.18 時已經開發完成,但是因為泛型帶來了巨大程式碼評審工作被推遲到了 Go 1.19。今年 8 月,Go 1.19 宣佈正式加入對 LoongArch(龍架構)的支持,至此,LoongArch 指令系統在 golang 社區成為與 X86、ARM 等指令系統並列支持的指令系統之一。其主要作者 Xiaodong Liu 正是 Go 中國貢獻者俱樂部的同學。
此次 LoongArch 架構得到 Golang 開源社區原生支持,意味著 LoongArch64 架構 Go 會隨著社區同步發展, 為各種雲原生項目、微服務架構、DevOps 平臺等遷移到 LoongArch 架構奠定了基礎, LoongArch 生態建設再次邁出堅實一步,為國際開源軟體發展注入中國創造新動力。

以上是來自龍芯官方的報道:http://www.loongeco.cn/article_822.htm
來自位元組的 sort 最佳化
來自位元組的同學使用了 pdqsort 演算法 + Go 1.18 泛型,實現了一個比標準庫 API 在幾乎所有情況下快 2x ~ 60x 的演算法庫。
pdqsort (pattern-defating quicksort) 是 Rust、C++ Boost 中默認的 unstable 排序演算法,其實質為一種混合排序演算法,會在不同情況下切換到不同的排序機制,是 C++ 標準庫演算法 introsort 的一種改進。可以認為是 unstable 混合排序演算法的較新成果。

以上是 C++ 的性能對比:其理想情況下的時間複雜度為 O(n),最壞情況下的時間複雜度為 O(n* logn),不需要額外的空間。論文地址:https://arxiv.org/pdf/2106.05123.pdf
Go 語言制霸騰訊
今年上半年,騰訊正式對外發布了《2021 年騰訊研發大資料包告》,披露了 2021 年騰訊在研發投入、研發效能、開源協同和技術公益等方面的重要資料。其中關於 Go 語言的重要資訊:隨著騰訊雲端運算和大資料相關業務的迅速發展,Go 語言快速增長,首次超越 C++,成為騰訊 2021 年最熱門的程式語言。

騰訊作為國內使用 Go 語言的大廠,不僅僅提供了很多 Go 語言項目。在 2021 年騰訊還發布了 Go 語言程式碼安全指南等基礎文件:

具體參考:https://github.com/Tencent/secguide
誕生自 Go 社區的國產程式語言
上次文章寫在 2019 年疫情之前,期間國內社區誕生了 Go+ 和凹語言兩個新的國產通用程式語言。其中 Go+ 是在 Go 語言之上做加法,而凹語言則是針對 WASM 場景對 Go 做減法。
目前 Go+ 已經舉行過幾次發佈會,多次上過 Hacker News 首頁,並且在 GitHub 獲得了 8k 多的 Star。而凹語言則開源不到半年只在小範圍分享過,不過也登上了 Hacker News 首頁,獲得了 500 多的 Star。
對於一個新語言來說,幾年時間都是比較短的。今年也參與了開源中國關於程式語言領域的點評,我希望這些項目能夠持續走下去,同時也希望能吸引更多的同學參與。

阿里/螞蟻 Go 語言相關的變化
因為作者目前在螞蟻工作,也近距離接觸了一些 Go 語言相關的事情,在此詳解螞蟻集團在 Go 語言相關的變化。這裡的列表並不完整,只是個人有接觸的幾個項目。
螞蟻發佈了 Go 程式設計規範
除了前端 JavaScript,在螞蟻后端開發 Go 已經成為僅次於 Java 到第二大語言。為了幫助更多的 Go 開發同學編寫更符合公司規範的程式碼,螞蟻發佈了 Go 語言程式設計規範 V1.0。

目前還在試用過程,希望可以早日可以公開分享。
Dubbogo 連接更多生態
Dubbo-go 項目由於雨在 2016 年創立;2018 年開始組建開源社區;2019 年項目正式進入 Apache 軟體基金會。經歷三年多不斷地迭代和最佳化,2021 年底 dubbogo 社區正式推出集成新通訊協議、新序列化協議、新應用註冊模型、新路由以及新的服務治理能力的 v3.0 版本,該版本在前期研發階段已經擁有了眾多生產使用者的關注和使用。
目前 Dubbo-go 生態覆蓋多種網路協議:Triple、Dubbo、JSONRPC、gRPC、HTTP、HTTP2 等。構建了 dubbogo proxyless mesh。
在 2022 年,dubbogo 社區實現即基於 RocketMQ 的安全 RPC 調用。並與騰訊北極星團隊合作,dubbogo 與 PolarisMesh 全面對接,實現了在 TCP/Triple/gRPC 層面的 TLS 安全通訊。

dubbogo 社區於 2020 年創建的 apache/dubbo-go-pixiu 項目,實現了 dubbo 網關與 dubbo mesh 的 sidecar,以 HTTP/gRPC 通訊方式解決了 dubbo 生態的多語言問題,已經被阿里集團和螞蟻集團等使用者在生產環境採用。在 2022 年與 dubbo 社區合作,融合 istio v1.14.3 後開始構建 dubbo mesh 的控制面,對接 dubbo proxyless mesh 和 dubbo mesh。
dubbogo 社區是阿里開源項目中最活躍的開源社區之一,多年的發展使社區積累了眾多熱愛開源的活躍貢獻者、 Apache Committer 將近 40 人,其中 PMC 成員 9 人。不僅給 Dubbo 以及其他 Dubbo 生態項目示範了通過社區的組織運營幫助項目發展,而且幫助了提升了整個 Dubbo 大社區的活躍度。
由 dubbogo 社區衍生的 seata-go 社區,於今年 11 月底發佈了 seata/seata-go 1.0 版本,實現了 seata 的 TCC/AT 兩種模式,預計到 12 月底將完成 XA 模式的對接。
MOSN 改進性能分析功能
MOSN 是螞蟻主要使用 Go 語言開發的雲原生網路代理平臺,在螞蟻集團有著幾十萬容器的大規模生產應用。在這種大規模的應用中,經常會遇到各種記憶體問題,通常情況下 pprof heap profile 可以很好幫助分析問題。今年 MOSN 在針對 Go 語言最佳化方面做了更深入的工作。
比如有時候 pprof 也不夠用,其實結合 MOSN 可以得到更多的資訊。下面結合 MOSN 產生的火焰圖:

具體細節可以參考文章:《Go 記憶體洩漏,pprof 夠用了麼?》
此外,螞蟻內部實踐的 moe 架構已經提交給 envoy 社區了,融合 C++ 和 Go 生態,預計這個月就可以合入 master 了:https://github.com/envoyproxy/envoy/pull/22573
Layotto 多運行時
Layotto 是構建在 MOSN 之上的項目,是螞蟻開源的多運行時項目。從 Layer otto 名字看就是第八層的意思,簡化後成了 Layotto,同時項目代號 L8(參考 Go 社區一個 otto 的 JavaScript 項目的名字,據說命名的靈感可能是來自 V8)。
Layotto 整體架構如下:

在下層對接了各種基礎設施,向上層應用提供了統一的,具有各種各樣分散式能力的標準 API。對於接入 Layotto 的應用來說,開發者不再需要關心底層各種元件的實現差異,只需要關注應用需要什麼樣的能力,然後調用對應能力的 API 即可,這樣可以徹底跟底層基礎設施解綁。
此外 Layotto 還提供了 Go 跟 Rust 版 WASM 的實現,雖然只支持 Demo 級功能,但已經足夠讓我們看到這種方案的潛在價值。
KusionStack 雲原生方向的探索
Go 語言最早的殺手級應用是 Docker、然後是 Kubernetes,可以說 Go 語言目前已經成為雲原生時代程式語言的標準。KusionStack 源於螞蟻集團內部的規模化工程化運維實踐,已廣泛應用在螞蟻多雲應用交付運維、計算及資料基礎設施交付、建站運維、資料庫運維等多個業務領域,助力螞蟻完成從傳統運維管理模式到可程式設計 DevOps 運維模式的轉型。
KusionStack 作為一個可程式設計、高靈活性的應用交付及運維技術棧(https://github.com/KusionStack/kusion)。靈感源於融合(Fusion)一詞,旨在幫助企業構建的應用運維配置管理平面及 DevOps 生態。
主要針對:融合專有云、混合雲、多雲混合場景;融合以雲原生技術為主,同時採用多種平臺技術的混合平臺技術選型;融合多項目、多團隊、多角色、多租戶、多環境的企業級訴求。

基於 Platform as Code (平臺服務即程式碼)理念,研發者可以快速收斂圍繞應用運維生命週期的全量配置定義,面向混合技術體系及雲環境,完成從應用運維研發到上線的端到端工作流程,真正做到一處編寫,隨處交付。目前,比如有贊等外部使用者已經通過 KusionStack 改造了很多中介軟體,並且在部署到諸多生產集群中。

中國 Gopher 踏上新徵程
中國作為世界上最大的 Gopher 地區,也同 Go 語言一樣走過了十幾個年頭。經過這麼多年的圍觀和參與,中國的 Gopher 已經逐漸成熟並有了自己的沉澱和理解。
目前 GitHub 上過萬 Star 的中國 Gopher 發起的開源項目就有 10 多個,比如:TiDB 代表的分散式資料庫;gogs、gitea 代表的 Git 服務平臺;比如 go-zero、beego 為代表的網路框架,excelize 提供的 Excel 處理、goproxy 提供的包代理服務等。也有很多中國 Gopher 給 Go 的 runtime 和編譯器後端提供了大量改進,包括對龍芯的支持等。
但是隨著參與的深入,融入國際開源項目的阻力也越來愈大:比如 Go2 的泛型設計為何沒有中國的聲音?Go2 為何還沒有解決中文名字不能匯出的問題?同一個 Issue 由中國社區提出和 Rob Pike 提出為何是兩種截然不同的反響(參考 Issue9097 和 Issue45624)?
因此我們需要開始思考如何拓展自己的生態:來自七牛的 Go+ 是國內第一個公開基於 Go 語言擴展的語言,其中一些創新思路值得借鑑;來自草根的凹語言則通過裁剪 Go 並結合 Wasm 社區發展出了自研的後端實現。我不敢說這些項目就一定能夠成功,但是這展現了中國 Gopher 們積極探索的態度。
未來已來,未來希望中國 Gopher 們能走的更穩一些!共勉!
作者介紹:
柴樹杉,KusionStack(https://github.com/KusionStack/kusion)項目開源負責人、凹語言作者。同時也是國內最早一批 Go 語言愛好者,創建了國內最早的關於 Go 程式設計的 QQ 群和 Google 論壇;同時也是 Go 程式碼貢獻者、Go 中國貢獻者俱樂部成員;組織翻譯了《Go語言聖經》、出版了《Go語言高級程式設計》《Go語言定製指南》等 Go 暢銷圖書。
參考連結
https://mp.weixin.qq.com/s/wE_z7MxDJjIVDN16578tDw
https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-contracts.md#why-not-use-the-syntax-like-c_and-java
https://go.dev/blog/survey2022-q2-results
https://golangcn.org/
https://arxiv.org/pdf/2106.05123.pdf
https://github.com/KusionStack/kusion
https://github.com/golang/go/issues/45624
https://github.com/envoyproxy/envoy/pull/22573
https://github.com/mosn/mosn
https://github.com/mosn/layotto