Go-фича: Dialer с контекстом
Часть серии Принято! В ней простыми словами объясняются новые фичи Go.
Методы net.Dialer для подключения по конкретным протоколам с поддержкой контекста.
Версия 1.26 • Станд. библиотека • Мелочь
Что
Тип net.Dialer подключается к сетевому адресу, используя указанный протокол (network) — TCP, UDP, IP или Unix-сокеты.
Новые методы Dialer с поддержкой контекста — DialTCP, DialUDP, DialIP и DialUnix — объединяют эффективную реализацию (как в существующих Dial-функциях) с возможностью отмены (как в Dialer.DialContext).
Зачем
В пакете net уже есть отдельные функции для разных протоколов (DialTCP, DialUDP, DialIP и DialUnix), но их сделали до появления context.Context, поэтому они не поддерживают отмену:
func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error)
func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error)
С другой стороны, у типа net.Dialer есть универсальный метод DialContext. Он поддерживает отмену и может использоваться для подключения по любому из поддерживаемых протоколов:
func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error)
Если вы уже знаете протокол и адрес, использовать DialContext менее эффективно, чем специализированные функции вроде DialTCP, по следующим причинам:
Резолв адреса:
DialContextсам выполняет разрешение адреса (например, DNS-запросы и преобразование вnet.TCPAddrилиnet.UDPAddr) на основе названия протокола и исходного адреса. Специализированные функции принимают уже готовый объект адреса, поэтому этот шаг пропускается.Роутинг по протоколу:
DialContextдолжен определить, какой протокол использовать, и направить вызов нужному обработчику. Специализированные функции уже знают, какой протокол нужен, и этот шаг тоже пропускается.
Таким образом, функции net.Dial* работают быстрее, но не поддерживают отмену операций. Тип net.Dialer поддерживает отмену, но работает медленнее. Решение — добавить в Dialer методы, которые учитывают контекст и работают с конкретными протоколами.
Заодно это позволит использовать новые типы адресов из пакета netip (например, netip.AddrPort вместо net.TCPAddr), которые более предпочтительны в современном Go-коде.
Как
Добавить четыре новых метода в net.Dialer:
DialTCP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*TCPConn, error)
DialUDP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*UDPConn, error)
DialIP(ctx context.Context, network string, laddr, raddr netip.Addr) (*IPConn, error)
DialUnix(ctx context.Context, network string, laddr, raddr *UnixAddr) (*UnixConn, error)
Сигнатуры методов похожи на существующие функции пакета net, но дополнительно принимают контекст и используют новые типы адресов из пакета netip.
Пример
Используем метод DialTCP, чтобы подключиться к TCP-серверу:
var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Подключение не пройдет, потому что сервер не запущен.
raddr := netip.MustParseAddrPort("127.0.0.1:12345")
conn, err := d.DialTCP(ctx, "tcp", netip.AddrPort{}, raddr)
if err != nil {
log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
if _, err := conn.Write([]byte("Hello, World!")); err != nil {
log.Fatal(err)
}
Failed to dial: dial tcp 127.0.0.1:12345: connect: connection refused (exit status 1)
Используем метод DialUnix, чтобы подключиться к Unix-сокету:
var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Подключение не пройдет, потому что сервер не запущен.
raddr := &net.UnixAddr{Name: "/path/to/unix.sock", Net: "unix"}
conn, err := d.DialUnix(ctx, "unix", nil, raddr)
if err != nil {
log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()
if _, err := conn.Write([]byte("Hello, socket!")); err != nil {
log.Fatal(err)
}
Failed to dial: dial unix /path/to/unix.sock: connect: no such file or directory (exit status 1)
В обоих случаях подключение не проходит, потому что я не запустил сервер в песочнице :)
Ссылки
★ Подписывайтесь на канал и проходите курсы.