Всем привет, я жареная рыба.

Когда я писал эту статью, был первый день нового года, и изначально я хотел сказать, что Go1.18 выйдет в этом месяце. Но, молодец, вышла Go1.18 beta2, и официал сообщил сообществу, что Go1.18 будет отложен до марта, гугугу...

Как показано ниже:

картина

Поэтому нам все еще нужно продолжать изучать новые функции.Сегодня Jianyu объединится с Брэдом Фитцпатриком « netaddr.IP: новый тип IP-адреса для Go [1] », чтобы показать вам причину появления новой сетевой библиотеки Go1.18 net/. неттип.

фон

Большой босс уходит в отставку

Брэд Фитцпатрик, который был в первоначальной команде разработчиков Go, работал в команде Go с 2010 по 2020 год и сменит компанию в 2021 году.

Следующий твит:

картина

Причина ухода: я слишком долго занимаюсь одним и тем же, мне немного скучно, я не хочу застрять в удобном затруднительном положении.

Теперь кажется, что я переключился на Tailscale для работы, связанной с WireGuard, и мне часто приходится иметь дело с сетевыми библиотеками.

Спрос рождается

Tailscale, написанный Big Brother, по сути является сетевым приложением, для работы с сетью и написанным на Go будет задействована стандартная библиотека net:

  • Используется для одного типа IP net.IP.
  • используется в сетевом представлении net.IPNet.

Образец кода:

import (
 "fmt"
 "net"
)

func main() {
 fmt.Println(net.IPv4(8888))
}

Выходной результат:

8.8.8.8

Когда Брэд Фитцпатрик на самом деле написал и использовал ее, он обнаружил, что есть много проблем с типом сетевой стандартной библиотеки, которую очень сложно использовать.

в чем проблема сейчас

Брэд Фитцпатрик перечислил проблемы стандартной библиотеки net.IP прямо в статье, с полной аргументацией.

Всего 7 основных вопросов:

  1. Это изменчиво. net.IPБазовым типом является []byte, что означает, что все, что вы ему передаете, может изменить его.
  2. Это не сравнимо. Поскольку срезы в Go несопоставимы, это означает net.IP, что ==сравнения операторов Go не поддерживаются и не могут использоваться в качестве ключей сопоставления.
  3. Он имеет два типа IP-адресов, которые могут раздражать при использовании net.IPили выборе.net.IPAddr
  4. Это огромная. Go net.IPсостоит 2 частей: 24-байтового заголовка слайса и 4/6-байтового IP-адреса. Если это так, поле Zone net.IPAddrтакже будет включено.
  5. Он будет выделять память в куче. Net-пакет Go распределен повсюду, что требует больше работы от сборщика мусора.
  6. Это не разборчиво. При анализе IP-адреса из строковой формы тип IP-адреса Go не может различать адреса IPv6, сопоставленные с IPv4, и адреса IPv4.
  7. Это прозрачный тип, net.IPопределяемый как: type IP []byte, является частью общедоступного API и не может быть изменен.

Брэд также упомянул, что некоторые из дизайнов были созданы в начале года, когда они были неопытными или плохо продуманными.

Теперь он ограничен обещанием совместимости с Go1 и не может быть изменен (обоюдоострый меч гарантий совместимости?).

Это реальная версия «Поедание собственной собачьей еды», поэтому в Tailscale он переделал еще одно колесо , inetaf/netaddr [2] , и хотел добавить его в стандартную библиотеку.

будущее, которое ты хочешь

Сравнительная таблица выглядит следующим образом:

характеристика Старая схема net.IP новый план
Неизменный ❌, ломтик
сопоставимый ❌, ломтик
Маленький след ❌, 28~56 байт ✅, фиксированные 24 байта
не размещается в куче
Поддерживает IPv4 и IPv6
Различие между IPv4 и IPv6
Поддержка зоны IPv6
непрозрачный тип
Взаимодействие со стандартной библиотекой 🤷, необходимо адаптировать метод

То, что я хочу, на самом деле является требованием от реального бизнеса Брэда, который должен поддержать 7 пунктов, упомянутых выше.

решение

текущий прогресс

Результат реализации, то есть новое решение, это библиотека inetaf/netaddr [3] (конечно, не исключено, что это результат теории вывода). И инициируйте вопросы и предложения в вопросах Go.

картина
https://pkg.go.dev/inet.af/netaddr

Расс Кокс инициировал обсуждение нового предложения « предложение: net/netaddr: добавить новый тип IP-адреса, пакет netaddr (обсуждение) [4] », которое было принято и внесено в новые функции Go1.18.

процесс восстановления

Каждое рассмотрение новой net/netipбиблиотеки Брэд подробно объяснил в статье.

Из-за нехватки места мы поделимся двумя из них.Заинтересованные партнеры могут прочитать аналитическую часть исходного текста.

комбинация типов интерфейса

С точки зрения сопоставимости интерфейс Go фактически поддерживает сравнение, то есть его можно использовать в качестве ключа карты для сравнения ==операторов .

Реализована первая версия схемы следующим образом и разработаны новые netaddr.IPтипы :

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 интерфейс ipImpl, который может не только поддерживать сравнение, но и не подвергаться воздействию внешнего мира (непрозрачный тип) и может поддерживать IPv6.

Новая проблема заключается в том, что, хотя он и меньше, чем нативная сеть, он все же не достигает цели, и у него все еще есть недостаток выделения памяти в куче.

Нераспределенные 24 байта

Если вы продолжите использовать интерфейс, вы не сможете решить основную цель (цель Брэда — 24 байта).

Поскольку интерфейс занимает 16 байт, оставшиеся 8 байт можно использовать, и следует разместить следующие вещи:

  • Семейство адресов (v4, v6 или ни то, ни другое, например: ноль для IP), требуется как минимум 2 бита.
  • Информация о зоне IPv6.

Тоже надо сравнивать, явно интерфейс не реализовать, т.к. информация адрес+зона считает количество байт, а на дисплей не хватает.

Нет возможности сделать это формально и явно, Брэд додумался использовать метод упаковки:

type IP struct {
   addr          [16]byte
   zoneAndFamily uint64
}

Но это означает, что необходимо вычислить количество цифр в поле zoneAndFamily, а затем вставить соответствующее значение, но это может быть не слишком сложно.

В конце концов Брэд придумал способ использовать указатели:

type IP struct {
    addr          [16]byte
    zoneAndFamily *T
}

Затем определите три соответствующих значения Sentinel для применения:

var (
     z0    *intern.Value        // 表示零值。
     z4    = new(intern.Value)  // 表示 IPv4 的哨位值
     z6noz = new(intern.Value)  // 表示 IPv6 的哨位值(没有 zone)。
)

Это фиксирует тип IP на 24 байта.

Подвести итог

Эта библиотека сетевых адресов обычно используется реже. Но Брэд Фитцпатрик приложил много усилий и исследований, чтобы дойти до конца.

В дополнение к функциям библиотеки есть много точек технической оптимизации, достойных нашего изучения и ссылки.Если вас интересует глубокая оптимизация, вы можете прочитать: https://tailscale.com/blog/netaddr-new- ip-type-for-go/ [ 5]

Новая библиотека net/netip, представленная в этой статье, появится как новая функция в Go1.18, и все желающие могут учиться и общаться вместе :)

использованная литература

[1]

netaddr.IP: новый тип IP-адреса для Go: https://tailscale.com/blog/netaddr-new-ip-type-for-go/

[2]

инетаф/нетаддр: https://github.com/inetaf/netaddr

[3]

инетаф/нетаддр: https://github.com/inetaf/netaddr

[4]

предложение: net/netaddr: добавить новый тип IP-адреса, пакет netaddr): 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

Подписывайтесь на Fried Fish, чтобы узнавать новости и информацию из первых рук👇

картина

картина

Здравствуйте, я Цзяньюй. Я опубликовал бестселлер Go "Путешествие программирования на языке Go", а затем получил награду GOP (самый самоуверенный эксперт в области Go). Нажмите на синее слово, чтобы увидеть мой книгоиздательский путь .

Ежедневно делитесь высококачественными статьями, публикуйте Go-интервью, опыт работы, архитектурный дизайн и присоединяйтесь к WeChat, чтобы привлечь читателей в группу обмена для общения со всеми!