加大力度!Go 將會(huì)增強(qiáng) Go1 向后兼容性
大家好,我是煎魚。
前段時(shí)間我們?cè)趯?Go1.20 新特性和變更時(shí),發(fā)現(xiàn)了一個(gè)問題有悖論。
Go1 有兼容性承諾,但如果發(fā)現(xiàn)了 BUG,會(huì)破壞兼容性。那怎么辦?是大膽修改,破壞掉,還是說(shuō)設(shè)計(jì)如此,打死不改?
寫了個(gè)開頭結(jié)果陽(yáng)了,現(xiàn)在陽(yáng)康還咳嗽回來(lái)接著更。
Go1 兼容性保障
在 Go1 引入了 Go 兼容性保障《Go 1 and the Future of Go Programs[1]》,也就是舊版本的 Go 程序也可以在繼續(xù) Go 的新版本中正確運(yùn)行。
當(dāng)然,凡事有例外,像是安全問題就是例外。
具體的完整細(xì)則如下圖:
我們常接觸到的有以下幾個(gè):
- 安全問題:Go 規(guī)范或?qū)嵤┲械陌踩珕栴}可能會(huì)被發(fā)現(xiàn),其解決需要破壞兼容性。將會(huì)保留解決這些安全問題的權(quán)利。
- 未指定的行為:雖然 Go 規(guī)范中嘗試明確所有已知行為,但是意料之外還是會(huì)存在一些方面是未定義的。這方面可能會(huì)出現(xiàn)問題。
- 規(guī)范錯(cuò)誤:如果有必要解決規(guī)范(spec)中出現(xiàn)的不一致、不完整,將會(huì)保留解決此類問題的權(quán)利。除了安全問題,不會(huì)對(duì)規(guī)范進(jìn)行不兼容的修改。
- 問題/缺陷:如果編譯器、庫(kù)有違反規(guī)范的缺陷,會(huì)保留修復(fù)這些缺陷的權(quán)利。
- 使用 import . 導(dǎo)入:如果在程序使用 import . "path",在未來(lái)的版本中,在導(dǎo)入的包中定義的其他名字可能會(huì)與程序中定義的其他名字相沖突。我們不推薦在測(cè)試之外使用 import .,使用它可能會(huì)導(dǎo)致程序在未來(lái)的版本中無(wú)法編譯。
- 引用 unsafe 庫(kù):導(dǎo)入 unsafe 的包可能依賴于 Go 實(shí)現(xiàn)的內(nèi)部屬性。會(huì)保留修改的權(quán)利。
Go 核心團(tuán)隊(duì)自述已經(jīng)有 10+ 年的 Go1 兼容性保障的經(jīng)驗(yàn),對(duì) Go 團(tuán)隊(duì)和用戶來(lái)說(shuō)都非常的有價(jià)值。
甚至近兩年,Go 團(tuán)隊(duì)和業(yè)內(nèi)把 Go 的高速發(fā)展歸因于對(duì) Go1 兼容性的保障的落地實(shí)施。
看起來(lái)還是有板有眼的。
擴(kuò)展 Go 向后兼容性
背景
雖然主觀上 Go 團(tuán)隊(duì)認(rèn)為做的比較好,但發(fā)現(xiàn)仍然存在進(jìn)行了兼容性破壞的情況。因此 Go 現(xiàn)任當(dāng)家 @Russ Cox 發(fā)起了《extending Go backward compatibility[2]》。
其認(rèn)為值得擴(kuò)展 Go1 的向后兼容性,以嘗試更少地破壞程序,明確地進(jìn)行 GODEBUG 的設(shè)置,便于聲明變更項(xiàng)在何時(shí)適應(yīng)使用和控制。
簡(jiǎn)單來(lái)講,就是 Go1 兼容性承諾給 Go 帶來(lái)了非常大的好處,要繼續(xù)擴(kuò)大優(yōu)勢(shì)項(xiàng),把長(zhǎng)板拉長(zhǎng)。
怎么突然提起
那為什么會(huì)突然想搞這事?因?yàn)?Russ Cox 最近和 Kubernetes 團(tuán)隊(duì)交流,發(fā)現(xiàn)在過(guò)去的幾年里,Go 平均每年大約會(huì)有一個(gè) Kubernetes 的破壞性變更。
其認(rèn)為 Kubernetes 肯定不是一個(gè)個(gè)例。雖然每年 1 次左右的頻率并不高,但 Go 團(tuán)隊(duì)在 Go1 兼容性的目標(biāo)是是 0 次。
以下是對(duì) Kubernetes 造成重大更改的一些示例:
有興趣的同學(xué)可以細(xì)看,考慮大多數(shù)同學(xué)可能并不關(guān)心,所以我沒有進(jìn)一步展開。
現(xiàn)有與兼容性相關(guān)的 GODEBUG 設(shè)置包括如下:
- GODEBUG=asyncpreemptoff=1:禁用基于信號(hào)的 Goroutine 搶占,這偶爾會(huì)發(fā)現(xiàn)操作系統(tǒng)的錯(cuò)誤。
- GODEBUG=cgocheck=0:禁用運(yùn)行時(shí)的 CGO 指針檢查。
- GODEBUG=cpu.<extension>=off:在運(yùn)行時(shí)禁止使用某個(gè)特定的 CPU 擴(kuò)展。
- GODEBUG=http2client=0:禁用客戶端的 HTTP/2。
- GODEBUG=http2server=0:禁用服務(wù)器端的 HTTP/2。
- GODEBUG=netdns=cgo:強(qiáng)制使用 CGO 解析器。
- GODEBUG=netdns=go:強(qiáng)制使用 Go DNS 解析器。
擴(kuò)大 Go1 兼容性保障
在新提案中,Go 將會(huì)正式確定并擴(kuò)大對(duì) GODEBUG 的使用,將根據(jù) go.mod 中的 Go 版本號(hào)來(lái)設(shè)置對(duì)應(yīng) GODEBUG,以提供超越當(dāng)前兼容性準(zhǔn)則所保證的兼容性。
根據(jù) go.mod 內(nèi)的 go 版本設(shè)置 GODEBUG
也就是接下來(lái)將會(huì)延伸以往的 GODEBUG 配置項(xiàng),擴(kuò)大使用面。
新措施的具體內(nèi)容如下:
- 承諾始終為兼容性指南允許的更改添加 GODEBUG 設(shè)置,但這仍然可能會(huì)破壞大量實(shí)際程序。
- 保證 GODEBUG 設(shè)置至少持續(xù) 2 年(4 個(gè)版本)。這只是最低要求;會(huì)存在例如,例如:http2server,可能會(huì)永遠(yuǎn)存在。
- 提供運(yùn)行時(shí)/指標(biāo)計(jì)數(shù)器,可用于觀察由 GODEBUG 設(shè)置導(dǎo)致的非默認(rèn)行為。如:/godebug/non-default-behavior/<name>:events。
- 根據(jù) Go modules 主模塊的 go.mod 中的 Go 版本,給 Go 應(yīng)用設(shè)置對(duì)應(yīng)的 GODEBUG 設(shè)置。注意不是當(dāng)前編譯的 Go 版本。是根據(jù) go.mod 內(nèi)的 Go 版本號(hào)。
- 允許使用以下形式的一行或多行覆蓋主包源代碼中的特定默認(rèn) GODEBUG 設(shè)置://go:debug <name>=<value>。
- 會(huì)同步修改 go/build、go list、go version -m 等配套工具鏈的使用,確保 GODEBUG 設(shè)置能夠被顯式查看。
- 在兼容性指南中記錄這些承諾以及如何配置使用 GODEBUG。
更加具體的案例,跟現(xiàn)有的 GODEBUG 其實(shí)是類似。例如 Go1.20 引入了一個(gè)新的 GODEBUG zipinsecurepath。
會(huì)遵循以下流程規(guī)范:
- Go1.20 中默認(rèn)值為 1,以保留舊的行為并允許不安全的路徑。
- Go1.21 可能會(huì)將默認(rèn)值更改為 0,以開始拒絕 archive/zip 中的不安全路徑。如果是這樣,且 Go1.21 也實(shí)現(xiàn)了這個(gè) GODEBUG 提案,那么當(dāng)使用 Go1.21 編譯的帶有 Go1.20 的模塊(go.mod)時(shí),將繼續(xù)允許不安全的路徑。只有當(dāng)這些模塊版本更新到 Go1.21 時(shí),它們才會(huì)開始拒絕不安全的路徑。
總結(jié)
Go 在這幾年對(duì) Go1 兼容性保障越來(lái)越看重,在今年將會(huì)進(jìn)一步加強(qiáng)。該提案已經(jīng)到了最終階段,很有可能會(huì)被接受,且最新評(píng)論沒有反對(duì)意見。
該提案將會(huì)加大在兼容性上 GODEBUG 的應(yīng)用,且最重要的是,將會(huì)根據(jù) go.mod 文件中的 Go 版本來(lái)調(diào)整 GODEBUG,這會(huì)是一個(gè)重大微調(diào)整。
唯一糾結(jié)的同學(xué),主要是反饋很多 Go 開發(fā)者,不知道自己修改 go.mod 文件中的 go 版本時(shí),會(huì)導(dǎo)致 GODEBUG 的變更,從而影響到程序,會(huì)比較隱晦。
想當(dāng)年,rsc 給 go.mod 加 go 版本號(hào)時(shí),表示還沒想好用在哪里...我只想表示這棵樹也埋的真深。
參考資料
[1]Go 1 and the Future of Go Programs: https://go.dev/doc/go1compat
[2]extending Go backward compatibility: https://github.com/golang/go/discussions/55090