<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          曹大吐槽 Go mod

          共 4630字,需瀏覽 10分鐘

           ·

          2021-03-07 21:50

          從 rsc 力排眾議設(shè)計(jì)并將 go mod 集成在 Go 語(yǔ)言中,已經(jīng)兩年過去了,時(shí)至今日,廣大 Gopher 還是經(jīng)常被 go mod 相關(guān)的問題折磨。

          本文會(huì)列舉一些我和我的同事使用 go mod 時(shí)碰到的問題,有些問題是 go mod 本身的問題,有些可能是第三方 goproxy 實(shí)現(xiàn)的問題。

          如果你做過比較大型的 go 項(xiàng)目開發(fā),相信總會(huì)有那么幾個(gè)讓你會(huì)心一笑。

          Go 命令的副作用

          從老版本一路升級(jí)過來(lái)的 gopher 很難理解為什么升級(jí)了新版本之后,go fmt 一個(gè)文件都變得非??D。

          go 的很多子命令都在引入 go mod 后增加了副作用,如 go test,go fmt(ide 常用),go build,go list(ide 常用)。


          例如上面的 go fmt,我只是想格式化一下我的文件,并沒有想下載依賴,但還是得耐心等依賴下載完畢。

          go test 時(shí)會(huì)自動(dòng)修改 go.mod 文件就更令人困惑了:why go mod keeps changing with go test[1],go.mod be modified after go test[2]

          這也是 go.mod 和 go.sum 為什么總是會(huì)出現(xiàn)在我們的文件變更列表里。何況這兩個(gè)文件在大項(xiàng)目開發(fā)的時(shí)候又尤其容易沖突。

          go.sum git 合并沖突

          當(dāng)很多同事在同一個(gè) git 倉(cāng)庫(kù)中做開發(fā)時(shí),即使我們已經(jīng)劃分好了工作職責(zé),在代碼合并的時(shí)候還是沒有辦法 auto merge:


          類似上面這樣的合并沖突,下面躺著 go.sum 的情況相信你也見過很多了。

          形同虛設(shè)的 semver 規(guī)范

          go mod 的設(shè)計(jì)認(rèn)為社區(qū)是嚴(yán)格遵守 semver 的規(guī)范的:

          Given a version number MAJOR.MINOR.PATCH, increment the: MAJOR version when you make incompatible API changes, MINOR version when you add functionality in a backwards compatible manner, and PATCH version when you make backwards compatible bug fixes.

          小版本升級(jí),如 1.7.4 -> 1.7.5 不應(yīng)該引入不兼容升級(jí),不過顯然 Google 高估了開源社區(qū)的節(jié)操。不少開源庫(kù)作者 API 修改起來(lái)都比較隨便。

          即使是 Google 自己的 grpc-go 項(xiàng)目,也在小版本升級(jí)中干過不兼容的事情:Update your SemVer Policy Please - Breaking changes in minor versions causing heartache[3]

          何況 grpc-go 的作者還光明正大地承認(rèn),他們?cè)?semver 的前提下,依然允許一些不兼容的 例外[4]。

          甚至還有那些從 release notes 中不易察覺的 behavior change[5] 導(dǎo)致依賴 grpc-go 的 helm 項(xiàng)目在生產(chǎn)環(huán)境中遇到了 bug,令人大為光火。

          好樣的,Google 工程師。

          除了人的問題之外,在 semver 規(guī)范中還存在一種例外情況:

          Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

          go mod 設(shè)計(jì)時(shí)并未考慮這種情況,mvs 算法在 0.y.z 范圍內(nèi)也會(huì)盡量在大版本不變的情況下,無(wú)情地幫你升級(jí)小版本,搞的百姓怨聲載道,苦不堪言。

          這兩年爆火的云原生領(lǐng)域,有很多項(xiàng)目在 0.x.y 版本一待就是兩三年。從業(yè)者依賴 0.x 的版本號(hào)再正常不過了。如果你問 go mod replace 誰(shuí)用的最溜?那想必是云原生開發(fā)者啦。

          版本信息擴(kuò)散

          由于 go mod 的設(shè)計(jì),如果一個(gè)依賴庫(kù)升級(jí)了新版本,我們的 import 路徑就會(huì)發(fā)生變化:


          chi 項(xiàng)目升級(jí) v5 了,所有引入 chi 下 lib 的代碼都需要改 import,開心不開心。我們又要升級(jí)兼容新的 API,又要改這些到處散落的 import path。

          這絕對(duì)不能說是優(yōu)秀的設(shè)計(jì)。

          goproxy 的實(shí)現(xiàn)各不相同

          因?yàn)樘厥庠颍瑖?guó)內(nèi)的 gopher 基本都需要配置國(guó)內(nèi)公司/個(gè)人開發(fā)的 goproxy 來(lái)加速依賴下載,這些 proxy 沒有使用相同的代碼,所以實(shí)現(xiàn)細(xì)節(jié)上經(jīng)常會(huì)有差別。

          例如,當(dāng)某個(gè)庫(kù)不存在時(shí),有的 goproxy 返回 404,而有的 goproxy 返回 500(這是筆者使用某司 goproxy 時(shí)的真實(shí)情況),匪夷所思。

          我們來(lái)看一下更加令人詫異的例子,來(lái)幫你理解這種匪夷所思。

          刪庫(kù)跑路

          簡(jiǎn)單做個(gè)實(shí)驗(yàn),遵從以下步驟:

          1. 在 github 上創(chuàng)建倉(cāng)庫(kù) A
          2. 通過 goproxy X 來(lái) go build
          3. 刪除倉(cāng)庫(kù) A
          4. 刪除 mod cache,并使用 goproxy X/Y/Z 分別執(zhí)行 go build
          第一次 go build刪庫(kù)后 goproxy.cn刪庫(kù)后 goproxy.io刪庫(kù)后 騰訊 goproxy刪庫(kù)后 aliyun goproxy
          goproxy.cn可 build不可 build不可 build不可 build
          goproxy.io可 build可 build不可 build不可 build
          騰訊 goproxy可 build不可 build可 build不可 build
          aliyun goproxy可 build不可 build不可 build可 build

          這次選取了國(guó)內(nèi)使用最廣泛的四個(gè) goproxy,使用其中之一緩存過一次的外部依賴,在刪庫(kù)后還是可以 build 的。但如果之前未經(jīng)該 goproxy 緩存的依賴,目前只有 goproxy.cn 依然能夠正常地下載依賴。

          經(jīng)過對(duì)原作者的咨詢,目前 goproxy.cn 在未找到依賴,但 gosumdb 中有值時(shí),會(huì)去官方的 index.golang.org 上進(jìn)行查找,而 gosumdb 中有值時(shí),一般情況下官方的 proxy.golang.org 中會(huì)有相應(yīng)的緩存(即使你設(shè)置的是第三方 goproxy)。這時(shí) goproxy.cn 也會(huì) 從官方 goproxy 中拉取,所以用戶的 build 還是能成功的。

          一個(gè)不帶 vendor 的項(xiàng)目,理論上就會(huì)出現(xiàn)因?yàn)?gopher 使用的 GOPROXY 不一樣,導(dǎo)致薛定諤的 build 結(jié)果。

          如果我們細(xì)看一下 sum.golang.org,官方對(duì)外部庫(kù)的緩存期限描述也是比較模糊的。

          模糊的存儲(chǔ)期限

          proxy.golang.org does not save all modules forever. There are a number of reasons for this, but one reason is if proxy.golang.org is not able to detect a suitable license. In this case, only a temporarily cached copy of the module will be made available, and may become unavailable if it is removed from the original source and becomes outdated. The checksums will still remain in the checksum database regardless of whether or not they have become unavailable in the mirror.

          上面這段話來(lái)自 sum.golang.org,從官方的這種說法來(lái)看,依賴庫(kù)在 goproxy 中的存儲(chǔ)并不是永久的,至少在 proxy.golang.org 中不是永久的,官方給出的 a number of reasons 也非常的模糊。

          我們沒有辦法把工作賭在這種虛無(wú)縹緲的措辭上,只能認(rèn)為 goproxy 不會(huì)永久緩存我們的倉(cāng)庫(kù)。沒有辦法指望我們的依賴能夠永遠(yuǎn)存在。原倉(cāng)庫(kù)從 github 消亡之后,遲早有一天也會(huì)在各個(gè) goproxy 上消亡,reproducible build 淪為笑談。

          即使在 go mod 推出的兩年后,對(duì)于我們來(lái)說,把依賴保存在 vendor 中依然是必要的。

          多年前,left pad 在 js 社區(qū)引起的悲劇,也許并沒有給當(dāng)前的軟件設(shè)計(jì)者提供多少教訓(xùn): how one programmer broke the internet[6],have we forgotten how to program[7]。

          [1]

          why go mod keeps changing with go test: https://stackoverflow.com/questions/61625796/why-go-mod-keeps-changing-with-go-test

          [2]

          go.mod be modified after go test: https://github.com/golang/go/issues/30921

          [3]

          Update your SemVer Policy Please - Breaking changes in minor versions causing heartache: https://github.com/grpc/grpc-go/issues/3798

          [4]

          例外: https://github.com/grpc/grpc-go/blob/master/Documentation/versioning.md

          [5]

          behavior change: https://codeengineered.com/blog/2018/golang-vgo-semver-human-error/

          [6]

          how one programmer broke the internet: https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/

          [7]

          have we forgotten how to program: https://www.davidhaney.io/npm-left-pad-have-we-forgotten-how-to-program/


          瀏覽 79
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  AV在线一区二区三区 | 真实国产亂伦免费看 | 囯产精品99久久久久久WWW | 亚洲婷婷六月天 | www.高清无码在线观看 |