Go1.18 新特性:引入新的 Netip 网络库
大家好,新特性引我是入新煎鱼。
写这篇文章时是网络大年初一,原本想说这个月就要发布 Go1.18 了。新特性引但是入新,好家伙,网络Go1.18 beta2 发布了,新特性引官方告知社区 Go1.18 要拖更到 3 月份了,入新咕咕咕...
如下图:
所以还是网络得继续学习新特性,今天煎鱼将结合 Brad Fitzpatrick 写的新特性引《netaddr.IP: a new IP address type for Go[1]》带大家了解 Go1.18 的新网络库 net/netip 的缘由。
背景
大佬离职
原本 Go 开发团队中的入新 Brad Fitzpatrick,在 2010~2020 年都在 Go 团队工作,网络在 2021 年起换公司了。新特性引
如下推特的入新消息:
离职的原因是:做了同样的东西太久了,有些厌烦,网络不想陷在一个舒适的困境中。
现在来看是换到了 Tailscale,做 WireGuard 相关工作,要经常与网络库打交道。
需求诞生
大佬公司写的高防服务器 Tailscale,本质上是一个网络应用程序,要与网络打交道,又是用 Go 写的,就会涉及到标准库 net:
在单个 IP 类型上使用 net.IP。网络表示上使用 net.IPNet。示例代码:
import (
"fmt"
"net"
)
func main() {
fmt.Println(net.IPv4(8, 8, 8, 8))
}输出结果:
8.8.8.8Brad Fitzpatrick 在实际编写和使用时,发现 net 标准库的类型有很多问题,很不好用。
现在有什么问题
Brad Fitzpatrick 对于标准库 net.IP 的问题,直接在文章中列举了出来,论据十足。
共 7 个大问题:
它是可变的。net.IP 的底层类型是 []byte,这意味着你传递给它的任何东西都可能改变它。它不具有可比性。因为 Go 中的 slice 不具有可比性,这意味着 net.IP 不支持 Go 的 == 运算符的对比,不能作为 map 的 key 来使用。服务器租用它有两种 IP 地址类型,要纠结用 net.IP,还是 net.IPAddr,要选择就会很烦人。它很大。Go 的 net.IP 包含 2 个部分,分别是 24 字节的 slice header 和 4/6 字节的 IP 地址。如果是 net.IPAddr 还会包含 Zone 字段。它会在堆上分配内存。Go 的 net 包到处都是分配,把更多的工作放在了 GC 上。它不可解析。从字符串形式解析 IP 时,Go 的 IP 类型无法区分 IPv4 映射的 IPv6 地址和 IPv4 地址。它是透明类型(transparent type),net.IP的定义是:type IP []byte,是其公共API的一部分,亿华云不可更改。Brad 也有提到有些是当年早期的设计,当时经验不足,或是没有考虑好。
现在受限于 Go1 兼容性承诺,已经无法改变了(兼容性保障的双刃剑?)。
这是个真实版 “Eating your own dog food”,所以在 Tailscale 他又重新造了一个轮子inetaf/netaddr[2],想贡献出来,塞进标准库里。
未来想要的样子
对比表格如下:
特性
老方案 net.IP
新方案
不变的
❌, slice
✅
可比的
❌, slice
✅
占用空间小
❌,28~56 字节
✅,固定 24 字节
不在堆上分配
❌
✅
支持 IPv4 和 IPv6
✅
✅
区分 IPv4 和 IPv6
❌
✅
支持 IPv6 区域
❌
✅
不透明的类型
❌
✅
与标准库互通
✅