自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 程序太大了,能要個延遲初始化不?

開發(fā) 前端
在一定規(guī)模下,大家認(rèn)為該影響是非常昂貴的。因為你可以看到只有 3 行的 Go 程序并沒有做任何實質(zhì)性的事情。

大家好,我是煎魚。

在公司的不斷發(fā)展中,一開始大多是大單體,改造慢了,一個倉庫會有使用十幾年的情況,倉庫的規(guī)模基本是不斷增大的過程。

影響之一就是會應(yīng)用程序打包后的體積越來越大,不知道被用哪里去了...今天要探討的提案《proposal: language: lazy init imports to possibly import without side effects[1]》,就與此有關(guān)。

提案

背景

我們來觀察一段很簡單的 Go 代碼,研究研究。如下代碼:

package main

import _ "crypto/x509"

func main() {}

這個 Go 程序只有 3 行代碼,看起來就沒有任何東西。實際上是這樣嗎?

我們可以執(zhí)行以下命令看看初始化過程:

$ go build --ldflags=--dumpdep main.go 2>&1 | grep inittask

輸出結(jié)果:

runtime.main -> runtime..inittask
runtime.main -> main..inittask
main..inittask -> crypto/x509..inittask
crypto/x509..inittask -> bytes..inittask
crypto/x509..inittask -> crypto/sha256..inittask
crypto/x509..inittask -> encoding/pem..inittask
crypto/x509..inittask -> errors..inittask
crypto/x509..inittask -> sync..inittask
crypto/x509..inittask -> crypto/aes..inittask
crypto/x509..inittask -> crypto/cipher..inittask
crypto/x509..inittask -> crypto/des..inittask
...
context..inittask -> context.init.0
vendor/golang.org/x/net/dns/dnsmessage..inittask -> vendor/golang.org/x/net/dns/dnsmessage.init
vendor/golang.org/x/net/route..inittask -> vendor/golang.org/x/net/route.init
vendor/golang.org/x/net/route..inittask -> vendor/golang.org/x/net/route.init.0
...

這段程序其實初始化了超級多的軟件包(標(biāo)準(zhǔn)庫、第三方包等)。使得包的的大小從標(biāo)準(zhǔn)的 1.3 MB 變成了 2.3 MB。

在一定規(guī)模下,大家認(rèn)為該影響是非常昂貴的。因為你可以看到只有 3 行的 Go 程序并沒有做任何實質(zhì)性的事情。

對啟動性能敏感的程序會比較難受,普通程序也會隨著日積月累進(jìn)入惡性循環(huán),啟動會比常規(guī)的更慢。

方案

在解決方案上我們結(jié)合另外一個提案《proposal: spec: Go 2: allow manual control over imported package initialization[2]》一起來看。

核心思想是:引入惰性初始化(lazy init),業(yè)內(nèi)也常稱為延遲加載。也就是必要的時候再真正的導(dǎo)入,不在引入包時就完成初始化。

優(yōu)化方向上:主要是在導(dǎo)入包路徑后增加懶惰初始化的聲明,例如在下方即將會提到的:go:lazyinit 或 go:deferred 注解。再等待程序真正使用到時再正式初始化。

1.go:lazyinit 的例子:

package main

import (
"crypto/x509" // go:lazyinit
"fmt"
)

func main() {...}

2.go:deferred 的例子:

package main

import (
_ "github.com/eddycjy/core" // go:deferred
_ "github.com/eddycjy/util" // go:deferred
)

func main() {
if os.Args[1] != "util" {
// 現(xiàn)在要使用這個包,開始初始化
core, err := runtime.InitDeferredImport("github.com/some/module/core")
...
}
...
}

以此來實現(xiàn),可以大大提高啟動性能。

討論

實際上在大多數(shù)的社區(qū)討論中,對這個提案是又愛又恨。因為它似乎又有合理的訴求,但細(xì)思似乎又會發(fā)現(xiàn)完全不對勁。

這個提案的背景和解決方案,是治標(biāo)不治本的。因為根本原因是:許多庫濫用了 init 函數(shù),讓許多不必要的東西都初始化了。

圖片

Go 核心開發(fā)團(tuán)隊認(rèn)為讓庫作者去修復(fù)這些庫,而不是讓 Go 來 “解決” 這些問題。如果支持惰性初始化,也會為這些低質(zhì)量庫的作者提供繼續(xù)這樣做的借口。

似曾相識的感覺

在寫這篇文章時,我想起了 Go 的依賴管理(Go modules),其有一個設(shè)計是基于語義化版本的規(guī)范。

如下圖:

圖片

版本格式為 “主版本號.次版本號.修訂號”,版本號的遞增規(guī)則如下:

  • 主版本號:當(dāng)你做了不兼容的 API 修改。
  • 次版本號:當(dāng)你做了向下兼容的功能性新增。
  • 修訂號:當(dāng)你做了向下兼容的問題修正。

Go modules 的原意是軟件庫都遵守這個規(guī)范,因此內(nèi)部會有最小版本選擇的邏輯。

也就是一個模塊往往依賴著許多其它許許多多的模塊,并且不同的模塊在依賴時很有可能會出現(xiàn)依賴同一個模塊的不同版本,Go 會把版本清單都整理出來,最終得到一個構(gòu)建清單。

如下圖:

圖片

你會發(fā)現(xiàn)最終構(gòu)建出來的依賴版本很有可能是與預(yù)期的不一致,從而導(dǎo)致許多業(yè)務(wù)問題。最經(jīng)典的就是 grpc-go、protoc-go、etcd 多版本兼容問題,讓許多人痛苦不已。

Go 團(tuán)隊在這一塊的設(shè)計是比較理想化的,曹大也將其歸類在 Go modules 的七宗罪之一了。而軟件包的 init 函數(shù)亂初始化一堆的問題,也是有些似曾相識了。

總結(jié)

這個問題的解決方案(提案)仍然在討論中,顯然 Go 團(tuán)隊更希望軟件庫的作者能夠約束好自己的代碼,不要亂初始化。

參考資料

[1]proposal: language: lazy init imports to possibly import without side effects: https://github.com/golang/go/issues/38450

[2]proposal: spec: Go 2: allow manual control over imported package initialization: https://github.com/golang/go/issues/48174

責(zé)任編輯:武曉燕 來源: 腦子進(jìn)煎魚了
相關(guān)推薦

2016-11-11 00:33:25

雙重檢查鎖定延遲初始化線程

2020-11-23 14:22:17

代碼Go存儲

2024-11-26 07:25:00

Rust初始化模式

2021-09-27 07:39:52

Go初始化函數(shù)package

2023-11-12 23:08:17

C++初始化

2011-06-17 15:29:44

C#對象初始化器集合初始化器

2019-11-04 13:50:36

Java數(shù)組編程語言

2009-09-08 09:48:34

LINQ初始化數(shù)組

2009-11-11 15:29:15

ADO初始化

2021-03-12 10:30:11

SpringMVC流程初始化

2010-07-28 10:22:33

FlexApplica

2022-07-06 10:37:45

SpringServlet初始化

2020-12-03 09:50:52

容器IoC流程

2024-01-15 06:34:09

Gin鏡像容器

2021-12-26 00:08:35

C++初始化列表

2009-09-18 11:15:52

C#數(shù)組初始化

2009-09-25 16:55:08

Hibernate初始

2009-12-16 14:04:04

Ruby對象初始化

2011-03-16 10:52:20

2013-03-25 10:38:24

ASP.NETHttpModule
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號