新聞中心
寫(xiě)這篇文章時(shí)是大年初一,原本想說(shuō)這個(gè)月就要發(fā)布 Go1.18 了。但是,好家伙,Go1.18 beta2 發(fā)布了,官方告知社區(qū) Go1.18 要拖更到 3 月份了,咕咕咕...

按需定制開(kāi)發(fā)可以根據(jù)自己的需求進(jìn)行定制,網(wǎng)站制作、成都網(wǎng)站制作構(gòu)思過(guò)程中功能建設(shè)理應(yīng)排到主要部位公司網(wǎng)站制作、成都網(wǎng)站制作的運(yùn)用實(shí)際效果公司網(wǎng)站制作網(wǎng)站建立與制做的實(shí)際意義
如下圖:
所以還是得繼續(xù)學(xué)習(xí)新特性,今天煎魚(yú)將結(jié)合 Brad Fitzpatrick 寫(xiě)的《netaddr.IP: a new IP address type for Go[1]》帶大家了解 Go1.18 的新網(wǎng)絡(luò)庫(kù) net/netip 的緣由。
背景
大佬離職
原本 Go 開(kāi)發(fā)團(tuán)隊(duì)中的 Brad Fitzpatrick,在 2010~2020 年都在 Go 團(tuán)隊(duì)工作,在 2021 年起換公司了。
如下推特的消息:
離職的原因是:做了同樣的東西太久了,有些厭煩,不想陷在一個(gè)舒適的困境中。
現(xiàn)在來(lái)看是換到了 Tailscale,做 WireGuard 相關(guān)工作,要經(jīng)常與網(wǎng)絡(luò)庫(kù)打交道。
需求誕生
大佬公司寫(xiě)的 Tailscale,本質(zhì)上是一個(gè)網(wǎng)絡(luò)應(yīng)用程序,要與網(wǎng)絡(luò)打交道,又是用 Go 寫(xiě)的,就會(huì)涉及到標(biāo)準(zhǔn)庫(kù) net:
- 在單個(gè) IP 類(lèi)型上使用 net.IP。
- 網(wǎng)絡(luò)表示上使用 net.IPNet。
示例代碼:
import (
"fmt"
"net"
)
func main() {
fmt.Println(net.IPv4(8, 8, 8, 8))
}
輸出結(jié)果:
8.8.8.8
Brad Fitzpatrick 在實(shí)際編寫(xiě)和使用時(shí),發(fā)現(xiàn) net 標(biāo)準(zhǔn)庫(kù)的類(lèi)型有很多問(wèn)題,很不好用。
現(xiàn)在有什么問(wèn)題
Brad Fitzpatrick 對(duì)于標(biāo)準(zhǔn)庫(kù) net.IP 的問(wèn)題,直接在文章中列舉了出來(lái),論據(jù)十足。
共 7 個(gè)大問(wèn)題:
- 它是可變的。net.IP 的底層類(lèi)型是 []byte,這意味著你傳遞給它的任何東西都可能改變它。
- 它不具有可比性。因?yàn)?Go 中的 slice 不具有可比性,這意味著 net.IP 不支持 Go 的 == 運(yùn)算符的對(duì)比,不能作為 map 的 key 來(lái)使用。
- 它有兩種 IP 地址類(lèi)型,要糾結(jié)用 net.IP,還是 net.IPAddr,要選擇就會(huì)很煩人。
- 它很大。Go 的 net.IP 包含 2 個(gè)部分,分別是 24 字節(jié)的 slice header 和 4/6 字節(jié)的 IP 地址。如果是 net.IPAddr 還會(huì)包含 Zone 字段。
- 它會(huì)在堆上分配內(nèi)存。Go 的 net 包到處都是分配,把更多的工作放在了 GC 上。
- 它不可解析。從字符串形式解析 IP 時(shí),Go 的 IP 類(lèi)型無(wú)法區(qū)分 IPv4 映射的 IPv6 地址和 IPv4 地址。
- 它是透明類(lèi)型(transparent type),net.IP的定義是:type IP []byte,是其公共API的一部分,不可更改。
Brad 也有提到有些是當(dāng)年早期的設(shè)計(jì),當(dāng)時(shí)經(jīng)驗(yàn)不足,或是沒(méi)有考慮好。
現(xiàn)在受限于 Go1 兼容性承諾,已經(jīng)無(wú)法改變了(兼容性保障的雙刃劍?)。
這是個(gè)真實(shí)版 “Eating your own dog food”,所以在 Tailscale 他又重新造了一個(gè)輪子inetaf/netaddr[2],想貢獻(xiàn)出來(lái),塞進(jìn)標(biāo)準(zhǔn)庫(kù)里。
未來(lái)想要的樣子
對(duì)比表格如下:
|
特性 |
老方案 net.IP |
新方案 |
|
不變的 |
, slice |
|
|
可比的 |
, slice |
|
|
占用空間小 |
,28~56 字節(jié) |
,固定 24 字節(jié) |
|
不在堆上分配 |
||
|
支持 IPv4 和 IPv6 |
||
|
區(qū)分 IPv4 和 IPv6 |
||
|
支持 IPv6 區(qū)域 |
||
|
不透明的類(lèi)型 |
||
|
與標(biāo)準(zhǔn)庫(kù)互通 |
????,需適配方法 |
想要的樣子,其實(shí)是 Brad 業(yè)務(wù)實(shí)戰(zhàn)出來(lái)的訴求,就是要支持前面提到的 7 點(diǎn)。
解決方案
當(dāng)前的進(jìn)展
實(shí)現(xiàn)的結(jié)果,也就是新方案做出來(lái)了,他就是inetaf/netaddr[3] 這個(gè)庫(kù)(當(dāng)然,也不排除是結(jié)果倒推理論)。并且在 Go issues 中發(fā)起 issues 和 proposal。
https://pkg.go.dev/inet.af/netaddr
Russ Cox 發(fā)起了新提案的討論《proposal: net/netaddr: add new IP address type, netaddr package (discussion)[4]》,并被接納,進(jìn)入了 Go1.18 的新特性當(dāng)中。
重造過(guò)程
新的 net/netip 庫(kù)的每一個(gè)考量點(diǎn),Brad 都在文章中有所詳細(xì)講解。
受限于篇幅,我們拿其中兩點(diǎn)來(lái)分享,有興趣的小伙伴可以閱讀原文的剖析部分。
接口類(lèi)型組合
在可比較這事上,Go 的接口(interface)其實(shí)是支持比較的,也就是可以作為 map 的 key 進(jìn)行 == 運(yùn)算符的比較。
實(shí)現(xiàn)了如下的第一版方案,設(shè)計(jì)了新的netaddr.IP 類(lèi)型:
type IP struct {
ipImpl
}
type ipImpl interface {
is4() bool
is6() bool
String() string
}
type v4Addr [4]byte
type v6Addr [16]byte
type v6AddrZone struct {
v6Addr
zone string
}上述代碼,在 IP 結(jié)構(gòu)體中增加了 ipImpl 接口,既能支持比較,還可以不對(duì)外暴露(不透明類(lèi)型),且可以支持 IPv6。
新的問(wèn)題在于,雖然比原生 net 小了,但還是沒(méi)達(dá)到目標(biāo),還是有在堆上分配的缺點(diǎn)。
免分配的 24 字節(jié)
如果繼續(xù)使用接口,是無(wú)法解決根本目標(biāo)(Brad 的目標(biāo)是 24 字節(jié))的。
因?yàn)榻涌?interface)占用 16 字節(jié),剩余 8 個(gè)字節(jié)可以用,要放如下東西:
- 地址族(v4、v6,或兩者都不是,如:IP 的零值),至少需要 2 位。
- IPv6 的 zone 信息。
還要能比較,顯然接口是無(wú)法實(shí)現(xiàn)的,因?yàn)榈刂?zone 信息算一下字節(jié)數(shù),顯示是不夠用的。
正規(guī)顯式的沒(méi)辦法,Brad 想到了用打包的方式:
type IP struct {
addr [16]byte
zoneAndFamily uint64
}但這么做,就意味著 zoneAndFamily 字段中需要計(jì)算位數(shù),再對(duì)應(yīng)的推入相應(yīng)的值,但也未必太折騰了。
最終 Brad 想到了,可以使用指針的方式:
type IP struct {
addr [16]byte
zoneAndFamily *T
}再定義 3 個(gè)對(duì)應(yīng)哨位值的來(lái)應(yīng)用:
var (
z0 *intern.Value // 表示零值。
z4 = new(intern.Value) // 表示 IPv4 的哨位值
z6noz = new(intern.Value) // 表示 IPv6 的哨位值(沒(méi)有 zone)。
)
這樣就可以把 IP 類(lèi)型固定在 24 字節(jié)。
總結(jié)
這個(gè)網(wǎng)絡(luò)地址庫(kù),一般都用的比較少。但是 Brad Fitzpatrick 在此投入了大量的精力和研究,達(dá)到了最終的目標(biāo)。
除去庫(kù)的功能外,有許多技術(shù)優(yōu)化點(diǎn)值得我們學(xué)習(xí)和參考,有興趣深入優(yōu)化部分的,可以閱讀:https://tailscale.com/blog/netaddr-new-ip-type-for-go/[5]
本文介紹的新 net/netip 庫(kù)將會(huì)在 Go1.18 中作為新特性出現(xiàn),歡迎大家一起學(xué)習(xí)交流:)
參考資料
[1]netaddr.IP: a new IP address type for Go: https://tailscale.com/blog/netaddr-new-ip-type-for-go/
[2]inetaf/netaddr: https://github.com/inetaf/netaddr
[3]inetaf/netaddr: https://github.com/inetaf/netaddr
[4]proposal: net/netaddr: add new IP address type, netaddr package): https://github.com/golang/go/discussions/47323
[5]https://tailscale.com/blog/netaddr-new-ip-type-for-go/: https://tailscale.com/blog/netaddr-new-ip-type-for-go/#wgcfg
網(wǎng)頁(yè)標(biāo)題:Go1.18新特性:引入新的Netip網(wǎng)絡(luò)庫(kù)
URL網(wǎng)址:http://fisionsoft.com.cn/article/dpoocjd.html


咨詢(xún)
建站咨詢(xún)
