原创分享 Go 1.16 新功能特性不完全前瞻

bigwhite-github · 2020年12月12日 · 最后由 1272881215 回复于 2020年12月14日 · 1798 次阅读
本帖已被设为精华帖!

Go 1.16 将于 2021 年 2 月发布。目前已经进入 freeze 状态,即不再接受新 feature,仅 fix bug、编写文档和接受安全更新等。

目前 Go 1.16 的发布说明尚处于早期草稿阶段,但 Go 团队成员正在致力于编写发布说明。Go 1.16 的完全特性列表说明还得等真正发布前才能得到。如今要了解 Go 1.16 功能特性都有哪些变化,只能结合现有的 release note 以及从Go 1.16 里程碑中的 issue 列表中挖掘。

下面就 “挖掘” 到的 Go 1.16 重要功能特性变化做简要的且不完全的前瞻。

1. 支持 Apple Silicon M1 芯片

Apple Silicon M1 芯片 Macbook 的发布让 Go 团队紧急为 Go 1.16 增加对 M1 的支持。如果要跨平台编译,只需设定 GOOS=darwin, GOARCH=arm64 即可构建出可以在搭载 M1 芯片的 Macbook 上运行的 Go 应用。

同时 Go 1.16 还增加了对 ios/amd64 的支持,主要是为了支持在 amd64 架构上的 MacOS 上运行 ios 模拟器。

2. RISC-V 架构支持 cgo 和-buildmode=pie

RISC-V 架构很可能是未来 5-10 年挑战 ARM 的最主要架构,Go 语言持续加大对 RISC-V 架构的支持,在 Go 1.16 中对 linux/riscv64 又增加了 cgo 支持以及-buildmode=pie。不过目前对 risc-v 仍仅限于 linux os。

3. 有关 go module 的变化

  • module-aware 模式成为默认状态。如要回到 gopath mode,将 GO111MODULE 设置为 auto;
  • go build 和 go test 不会修改 go.mod 和 go.sum 文件。能修改这两个文件的命令只有 go get 和 go mod tidy;
  • go get 之前的构建和安装 go 包的行为模式将被废弃。go get 将专注于分析依赖,并获取 go 包/module,更新 go.mod/go.sum;
  • go install 将恢复自己构建和安装包的 “角色”(在 go module 加入后,go install 日益受到冷落,这次翻身了);
  • go.mod 将支持 retract 指示符,包或 module 作者可以利用该指示符在自己 module 的 go.mod 中标记某些版本撤回 (因不安全、不兼容或损坏等原因),不建议使用。
  • go.mod 中的 exclude 指示符语义变更:Go 1.16 中将忽略 exclude 指示的 module/包依赖;而之前的版本 go 工具链仅仅是跳过 exclude 指示的版本,而使用该依赖包/module 的下一个版本。
  • -i build flag 废弃;
  • go get 的-insecure 命令行标志选项作废,可以用 GOINSECURE 环境变量指示 go get 是否通过不安全的 http 去获取包;

4. 支持在 Go 二进制文件中嵌入静态文件 (文本、图片等)

Go 1.16 新增go:embed指示符和 embed 标准库包,二者一起用于支持在在 Go 二进制文件中嵌入静态文件。下面是一个在 Go 应用中嵌入文本文件用于 http 应答内容的小例子:

// hello.txt
hello, go 1.16

// main.go
package main

import (
         _  "embed"
    "net/http"
)

//go:embed hello.txt
var s string

func main() {
    http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(s))
    }))
    http.ListenAndServe(":8080", nil)
}

上述源码中的go:embed指示符的含义是:将 hello.txt 内容存储在字符串变量 s 中。我们构建该源码,并验证一下 s 中存储的是否是 hello.txt 中的数据:

$ go build -o demo main.go
$ mv hello.txt hello.txt.bak // 将hello.txt改名,我们看看数据是否真的已经嵌入到二进制文件demo中了
$ ./demo

$curl localhost:8080
hello, go 1.16

5.GODEBUG 环境变量支持跟踪

当 GODEBUG 环境变量包含 inittrace=1 时,Go 运行时将会报告各个源代码文件中的 init 函数的执行时间和内存开辟消耗情况。比如对于上面的程序 demo,我们按如下命令执行:

# GODEBUG=inittrace=1 ./demo
init internal/bytealg @0.014 ms, 0 ms clock, 0 bytes, 0 allocs
init runtime @0.033 ms, 0.015 ms clock, 0 bytes, 0 allocs
init errors @0.24 ms, 0.003 ms clock, 0 bytes, 0 allocs
init sync @0.47 ms, 0.001 ms clock, 16 bytes, 1 allocs
init io @0.66 ms, 0 ms clock, 144 bytes, 9 allocs
init internal/oserror @0.85 ms, 0 ms clock, 80 bytes, 5 allocs
init syscall @1.0 ms, 0.006 ms clock, 624 bytes, 2 allocs
init time @1.2 ms, 0.013 ms clock, 384 bytes, 8 allocs
init path @1.4 ms, 0.003 ms clock, 16 bytes, 1 allocs
init io/fs @1.6 ms, 0 ms clock, 16 bytes, 1 allocs
init context @2.3 ms, 0.002 ms clock, 128 bytes, 4 allocs
init math @2.5 ms, 0 ms clock, 0 bytes, 0 allocs
init strconv @2.7 ms, 0 ms clock, 32 bytes, 2 allocs
init unicode @2.9 ms, 0.065 ms clock, 23736 bytes, 26 allocs
init bytes @3.2 ms, 0 ms clock, 48 bytes, 3 allocs
init crypto @3.3 ms, 0.001 ms clock, 160 bytes, 1 allocs
init reflect @3.5 ms, 0.002 ms clock, 0 bytes, 0 allocs
init encoding/binary @3.7 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/cipher @3.8 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/aes @4.0 ms, 0.003 ms clock, 16 bytes, 1 allocs
init internal/poll @4.1 ms, 0 ms clock, 64 bytes, 4 allocs
init os @4.2 ms, 0.029 ms clock, 544 bytes, 13 allocs
init fmt @4.4 ms, 0.003 ms clock, 32 bytes, 2 allocs
init math/rand @4.5 ms, 0.023 ms clock, 5440 bytes, 3 allocs
init math/big @4.7 ms, 0.002 ms clock, 32 bytes, 2 allocs
init crypto/sha512 @4.8 ms, 0.004 ms clock, 0 bytes, 0 allocs
init encoding/asn1 @5.0 ms, 0.004 ms clock, 224 bytes, 7 allocs
init vendor/golang.org/x/crypto/cryptobyte @5.1 ms, 0 ms clock, 48 bytes, 2 allocs
init crypto/ecdsa @5.3 ms, 0 ms clock, 48 bytes, 3 allocs
init bufio @5.4 ms, 0.003 ms clock, 176 bytes, 11 allocs
init crypto/rand @5.6 ms, 0.001 ms clock, 120 bytes, 4 allocs
init crypto/rsa @5.7 ms, 0.007 ms clock, 648 bytes, 18 allocs
init crypto/sha1 @5.8 ms, 0 ms clock, 0 bytes, 0 allocs
init crypto/sha256 @5.9 ms, 0 ms clock, 0 bytes, 0 allocs
init encoding/base64 @5.9 ms, 0.006 ms clock, 1408 bytes, 4 allocs
init crypto/md5 @6.0 ms, 0 ms clock, 0 bytes, 0 allocs
init encoding/hex @6.1 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/x509/pkix @6.1 ms, 0.001 ms clock, 624 bytes, 2 allocs
init path/filepath @6.2 ms, 0 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/net/dns/dnsmessage @6.3 ms, 0.009 ms clock, 1616 bytes, 27 allocs
init net @6.3 ms, 0.029 ms clock, 2840 bytes, 74 allocs
init crypto/dsa @6.5 ms, 0 ms clock, 16 bytes, 1 allocs
init crypto/x509 @6.5 ms, 0.016 ms clock, 4768 bytes, 15 allocs
init io/ioutil @6.7 ms, 0.002 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/sys/cpu @6.7 ms, 0.009 ms clock, 1280 bytes, 1 allocs
init vendor/golang.org/x/crypto/chacha20poly1305 @6.8 ms, 0 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/crypto/curve25519 @6.9 ms, 0 ms clock, 0 bytes, 0 allocs
init crypto/tls @7.0 ms, 0.007 ms clock, 1600 bytes, 11 allocs
init log @7.0 ms, 0 ms clock, 80 bytes, 1 allocs
init mime @7.1 ms, 0.008 ms clock, 1232 bytes, 4 allocs
init mime/multipart @7.2 ms, 0.001 ms clock, 192 bytes, 4 allocs
init compress/flate @7.3 ms, 0.012 ms clock, 4240 bytes, 7 allocs
init hash/crc32 @7.4 ms, 0.014 ms clock, 1024 bytes, 1 allocs
init compress/gzip @7.5 ms, 0 ms clock, 32 bytes, 2 allocs
init vendor/golang.org/x/text/transform @7.5 ms, 0 ms clock, 80 bytes, 5 allocs
init vendor/golang.org/x/text/unicode/bidi @7.6 ms, 0.005 ms clock, 272 bytes, 2 allocs
init vendor/golang.org/x/text/secure/bidirule @7.7 ms, 0.008 ms clock, 16 bytes, 1 allocs
init vendor/golang.org/x/text/unicode/norm @7.8 ms, 0.002 ms clock, 0 bytes, 0 allocs
init vendor/golang.org/x/net/idna @7.8 ms, 0 ms clock, 0 bytes, 0 allocs
init vendor/golang.org/x/net/http/httpguts @7.9 ms, 0.002 ms clock, 848 bytes, 3 allocs
init vendor/golang.org/x/net/http2/hpack @7.9 ms, 0.063 ms clock, 22440 bytes, 32 allocs
init net/http/internal @8.1 ms, 0.005 ms clock, 1808 bytes, 3 allocs
init vendor/golang.org/x/net/http/httpproxy @8.2 ms, 0 ms clock, 336 bytes, 2 allocs
init net/http @8.3 ms, 0.026 ms clock, 10280 bytes, 113 allocs

我们看到各个依赖包中的 init 函数执行的消耗情况都被输出了出来,根据这些信息,我们可以很容易判断出 init 函数中可能存在的性能问题或瓶颈。

6. 链接器进一步优化

Go 1.15实现了 go linker 的第一阶段优化后,Go 1.16 中继续实施了对 linker 的第二阶段优化。优化后的链接器要平均比 Go 1.15 的快 20%-25%,消耗的内存却减少 5%-15%。

7. struct field 的 tag 中的多个 key 可以合并写

如果某个结构体支持多种编码格式的序列化和反序列化,比如:json、bson、xml,那么之前版本需要按如下书写该结构体的字段 tag,冗长且重复:

type MyStruct struct {
  Field1 string `json:"field_1,omitempty" bson:"field_1,omitempty" xml:"field_1,omitempty" form:"field_1,omitempty" other:"value"`
}

Go 1.16 支持将多个 key 进行合并,上面的 tag 可以写成如下形式:

type MyStruct struct {
  Field1 string `json bson xml form:"field_1,omitempty" other:"value"`
}

8. 其他改变

  • 新增 runtime/metrics 包,以替代 runtime.ReadMemStats 和 debug.ReadGCStats 输出 runtime 的各种度量数据,这个包更通用稳定,性能也更好;
  • 新增 io/fs 包,用于提供只读的操作 os 的文件树的高级接口;
  • 对 Unicode 标准的支持从 12.0.0 升级为 13.0.0。

附录:安装 go tip 版本的两种方式

1) 从源码安装

$git clone https//github.com/golang/go.git
$cd go/src
$./all.bash

2) 使用 gotip 工具安装

$go get golang.org/dl/gotip
$gotip download

Go 技术专栏 “改善 Go 语⾔编程质量的 50 个有效实践” 主要满足广大 gopher 关于 Go 语言进阶的需求,围绕如何写出地道且高质量 Go 代码给出 50 条有效实践建议,上线后收到一致好评!78 元简直就是白菜价,简直就是白 piao! 欢迎大家订阅!

我的网课 “Kubernetes 实战:高可用集群搭建、配置、运维与应用” 在慕课网热卖中,欢迎小伙伴们订阅学习!

“Gopher 部落” 星球首开,福利自然是少不了的!2020 年年底之前,8.8 折加入星球。扫描下方图片二维码即享优惠!

Gopher Daily(Gopher 每日新闻) 归档仓库 - https://github.com/bigwhite/gopherdaily

我的联系方式:

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
kevin 将本帖设为了精华贴 12月13日 08:55
kevin GoCN 每日新闻 (2020-12-13) 中提及了此贴 12月13日 09:08

期待 Go 1.16,最喜欢的莫过于 go:embed😁

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册