برنامه نویسی

شیرجه عمیق به روشهای addrport net/netip 6/7

سلام! در مقاله قبلی ما ، روشهای ADDR را با جزئیات بررسی کردیم. حال بیایید عمیق به روش های addrport شیرجه بزنیم. Addrport هنگام کار با سرویس های شبکه یک نوع مهم است زیرا یک آدرس IP را با شماره پورت ترکیب می کند. ما هر روش را با نمونه های عملی و موارد استفاده در دنیای واقعی کشف خواهیم کرد.

کاوش در روش اصلی

ابتدا بیایید به تمام راه های کار با Addrport نگاه کنیم.

خلقت و تجزیه

package main

import (
    "fmt"
    "net/netip"
)

func demoAddrPortCreation() {
    // From string
    ap1, _ := netip.ParseAddrPort("192.168.1.1:8080")

    // From Addr and port
    addr := netip.MustParseAddr("192.168.1.1")
    ap2 := netip.AddrPortFrom(addr, 8080)

    // From raw IP and port
    ap3 := netip.AddrPortFrom(
        netip.AddrFrom4([4]byte{192, 168, 1, 1}),
        8080,
    )

    fmt.Printf("From string: %v\n", ap1)
    fmt.Printf("From components: %v\n", ap2)
    fmt.Printf("From raw: %v\n", ap3)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

روشهای دسترسی به مؤلفه

func exploreComponents(ap netip.AddrPort) {
    // Get the address part
    addr := ap.Addr()
    fmt.Printf("Address: %v\n", addr)

    // Get the port number
    port := ap.Port()
    fmt.Printf("Port: %d\n", port)

    // Check validity
    fmt.Printf("Is valid: %v\n", ap.IsValid())

    // String representation
    str := ap.String()
    fmt.Printf("String form: %s\n", str)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برنامه های کاربردی

1. سیستم کشف خدمات

در اینجا یک اجرای اکتشاف قوی با استفاده از addrport وجود دارد:

type ServiceType string

const (
    ServiceHTTP  ServiceType = "http"
    ServiceHTTPS ServiceType = "https"
    ServiceGRPC  ServiceType = "grpc"
)

type ServiceInstance struct {
    ID        string
    Type      ServiceType
    Endpoint  netip.AddrPort
    Metadata  map[string]string
    LastSeen  time.Time
}

type ServiceRegistry struct {
    services map[ServiceType]map[string]*ServiceInstance
    mu       sync.RWMutex
}

func NewServiceRegistry() *ServiceRegistry {
    return &ServiceRegistry{
        services: make(map[ServiceType]map[string]*ServiceInstance),
    }
}

func (sr *ServiceRegistry) Register(instance *ServiceInstance) error {
    sr.mu.Lock()
    defer sr.mu.Unlock()

    if !instance.Endpoint.IsValid() {
        return fmt.Errorf("invalid endpoint")
    }

    // Initialize type map if needed
    if sr.services[instance.Type] == nil {
        sr.services[instance.Type] = make(map[string]*ServiceInstance)
    }

    // Update or add instance
    instance.LastSeen = time.Now()
    sr.services[instance.Type][instance.ID] = instance

    return nil
}

func (sr *ServiceRegistry) Discover(stype ServiceType) []*ServiceInstance {
    sr.mu.RLock()
    defer sr.mu.RUnlock()

    var instances []*ServiceInstance
    for _, instance := range sr.services[stype] {
        instances = append(instances, instance)
    }

    return instances
}

func (sr *ServiceRegistry) Cleanup(maxAge time.Duration) {
    sr.mu.Lock()
    defer sr.mu.Unlock()

    now := time.Now()
    for stype, typeMap := range sr.services {
        for id, instance := range typeMap {
            if now.Sub(instance.LastSeen) > maxAge {
                delete(typeMap, id)
            }
        }

        if len(typeMap) == 0 {
            delete(sr.services, stype)
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. مدیر استخر اتصال

استخر اتصال که از Addrport برای ردیابی نقطه پایانی استفاده می کند:

type ConnState int

const (
    ConnIdle ConnState = iota
    ConnInUse
    ConnBroken
)

type PooledConn struct {
    Conn      net.Conn
    State     ConnState
    LastUsed  time.Time
    UseCount  int
}

type ConnectionPool struct {
    endpoints map[netip.AddrPort][]*PooledConn
    mu        sync.RWMutex
    maxIdle   time.Duration
    maxUses   int
}

func NewConnectionPool(maxIdle time.Duration, maxUses int) *ConnectionPool {
    return &ConnectionPool{
        endpoints: make(map[netip.AddrPort][]*PooledConn),
        maxIdle:   maxIdle,
        maxUses:   maxUses,
    }
}

func (cp *ConnectionPool) GetConnection(endpoint netip.AddrPort) (net.Conn, error) {
    cp.mu.Lock()
    defer cp.mu.Unlock()

    // Look for available connection
    conns := cp.endpoints[endpoint]
    for _, pc := range conns {
        if pc.State == ConnIdle {
            if time.Since(pc.LastUsed) > cp.maxIdle || pc.UseCount >= cp.maxUses {
                // Connection too old or overused - close and remove
                pc.Conn.Close()
                continue
            }

            pc.State = ConnInUse
            pc.LastUsed = time.Now()
            pc.UseCount++
            return pc.Conn, nil
        }
    }

    // Create new connection
    conn, err := net.Dial("tcp", endpoint.String())
    if err != nil {
        return nil, fmt.Errorf("failed to connect to %v: %w", endpoint, err)
    }

    pc := &PooledConn{
        Conn:     conn,
        State:    ConnInUse,
        LastUsed: time.Now(),
        UseCount: 1,
    }

    cp.endpoints[endpoint] = append(cp.endpoints[endpoint], pc)
    return conn, nil
}

func (cp *ConnectionPool) ReleaseConnection(endpoint netip.AddrPort, conn net.Conn) {
    cp.mu.Lock()
    defer cp.mu.Unlock()

    for _, pc := range cp.endpoints[endpoint] {
        if pc.Conn == conn {
            pc.State = ConnIdle
            pc.LastUsed = time.Now()
            return
        }
    }
}

func (cp *ConnectionPool) Cleanup() {
    cp.mu.Lock()
    defer cp.mu.Unlock()

    now := time.Now()
    for endpoint, conns := range cp.endpoints {
        var active []*PooledConn
        for _, pc := range conns {
            if pc.State == ConnIdle && 
               (now.Sub(pc.LastUsed) > cp.maxIdle || pc.UseCount >= cp.maxUses) {
                pc.Conn.Close()
                continue
            }
            active = append(active, pc)
        }

        if len(active) == 0 {
            delete(cp.endpoints, endpoint)
        } else {
            cp.endpoints[endpoint] = active
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. اجرای متعادل کننده بار

یک متعادل کننده بار با استفاده از addrport برای مدیریت باطن:

type Backend struct {
    Endpoint     netip.AddrPort
    Weight       int
    Connections  int64
    LastChecked  time.Time
    Healthy      bool
}

type LoadBalancer struct {
    backends []*Backend
    mu       sync.RWMutex
}

func NewLoadBalancer() *LoadBalancer {
    return &LoadBalancer{}
}

func (lb *LoadBalancer) AddBackend(endpoint netip.AddrPort, weight int) error {
    if !endpoint.IsValid() {
        return fmt.Errorf("invalid endpoint")
    }

    lb.mu.Lock()
    defer lb.mu.Unlock()

    // Check for duplicate
    for _, b := range lb.backends {
        if b.Endpoint == endpoint {
            return fmt.Errorf("backend already exists")
        }
    }

    lb.backends = append(lb.backends, &Backend{
        Endpoint:    endpoint,
        Weight:      weight,
        LastChecked: time.Now(),
        Healthy:     true,
    })

    return nil
}

func (lb *LoadBalancer) RemoveBackend(endpoint netip.AddrPort) {
    lb.mu.Lock()
    defer lb.mu.Unlock()

    for i, b := range lb.backends {
        if b.Endpoint == endpoint {
            // Remove backend
            lb.backends = append(lb.backends[:i], lb.backends[i+1:]...)
            return
        }
    }
}

func (lb *LoadBalancer) GetBackend() (*Backend, error) {
    lb.mu.RLock()
    defer lb.mu.RUnlock()

    var totalWeight int
    var candidates []*Backend

    for _, b := range lb.backends {
        if b.Healthy {
            candidates = append(candidates, b)
            totalWeight += b.Weight
        }
    }

    if len(candidates) == 0 {
        return nil, fmt.Errorf("no healthy backends available")
    }

    // Weighted random selection
    target := rand.Intn(totalWeight)
    currentWeight := 0

    for _, b := range candidates {
        currentWeight += b.Weight
        if target < currentWeight {
            atomic.AddInt64(&b.Connections, 1)
            return b, nil
        }
    }

    // Shouldn't reach here, but just in case
    b := candidates[len(candidates)-1]
    atomic.AddInt64(&b.Connections, 1)
    return b, nil
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بهترین روشها

  1. اعتبار سنجی
    همیشه قبل از استفاده ، addrport را تأیید کنید:
   func validateEndpoint(ep netip.AddrPort) error {
       if !ep.IsValid() {
           return fmt.Errorf("invalid endpoint")
       }

       if ep.Addr().IsUnspecified() {
           return fmt.Errorf("unspecified address not allowed")
       }

       return nil
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. دست زدن به رشته
    با تبدیل رشته ها مراقب باشید:
   func formatEndpoint(ep netip.AddrPort) string {
       if ep.Addr().Is6() {
           // IPv6 addresses need brackets
           return fmt.Sprintf("[%s]:%d", ep.Addr(), ep.Port())
       }
       return ep.String()
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. اعتبار سنجی محدوده بندر
    در صورت لزوم شماره پورت را بررسی کنید:
   func isUserPort(ep netip.AddrPort) bool {
       port := ep.Port()
       return port >= 1024 && port <= 49151
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

الگوهای مشترک

  1. پیکربندی خدمات
   type ServiceConfig struct {
       Listen    netip.AddrPort
       Upstream  []netip.AddrPort
   }

   func parseConfig(config map[string]string) (ServiceConfig, error) {
       var cfg ServiceConfig

       if listen, err := netip.ParseAddrPort(config["listen"]); err != nil {
           return cfg, fmt.Errorf("invalid listen address: %w", err)
       } else {
           cfg.Listen = listen
       }

       for _, up := range strings.Split(config["upstream"], ",") {
           if endpoint, err := netip.ParseAddrPort(up); err != nil {
               return cfg, fmt.Errorf("invalid upstream %q: %w", up, err)
           } else {
               cfg.Upstream = append(cfg.Upstream, endpoint)
           }
       }

       return cfg, nil
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. رسیدگی به خانواده
   func getDialNetwork(ep netip.AddrPort) string {
       if ep.Addr().Is6() {
           return "tcp6"
       }
       return "tcp4"
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نکات مربوط به عملکرد

  1. از تجزیه رشته خودداری کنید
   // Bad
   ep, _ := netip.ParseAddrPort(fmt.Sprintf("%s:%d", addr, port))

   // Good
   ep := netip.AddrPortFrom(addr, port)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. انباره کارآمد
   // Bad
   map[string]string  // Using string representations

   // Good
   map[netip.AddrPort]interface{}  // Using AddrPort directly
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. عملیات دسته
   func batchConnect(endpoints []netip.AddrPort) []net.Conn {
       var wg sync.WaitGroup
       conns := make([]net.Conn, len(endpoints))

       for i, ep := range endpoints {
           wg.Add(1)
           go func(i int, ep netip.AddrPort) {
               defer wg.Done()
               if conn, err := net.Dial("tcp", ep.String()); err == nil {
                   conns[i] = conn
               }
           }(i, ep)
       }

       wg.Wait()
       return conns
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

چه چیزی بعدی؟

در مقاله بعدی ما ، روشهای پیشوند را به عمق بررسی خواهیم کرد و بررسی دقیق ما از انواع اصلی در NET/NETIP را انجام می دهیم. خواهیم دید که چگونه می توان با نمادهای CIDR و عملیات زیر شبکه به طور مؤثر کار کرد.

تا آن زمان ، آزمایش را با Addrport ادامه دهید! این یک ساختمان اساسی برای خدمات شبکه در GO است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا