شیرجه عمیق به روشهای 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
}
بهترین روشها
-
اعتبار سنجی
همیشه قبل از استفاده ، 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
}
-
دست زدن به رشته
با تبدیل رشته ها مراقب باشید:
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()
}
-
اعتبار سنجی محدوده بندر
در صورت لزوم شماره پورت را بررسی کنید:
func isUserPort(ep netip.AddrPort) bool {
port := ep.Port()
return port >= 1024 && port <= 49151
}
الگوهای مشترک
- پیکربندی خدمات
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
}
- رسیدگی به خانواده
func getDialNetwork(ep netip.AddrPort) string {
if ep.Addr().Is6() {
return "tcp6"
}
return "tcp4"
}
نکات مربوط به عملکرد
- از تجزیه رشته خودداری کنید
// Bad
ep, _ := netip.ParseAddrPort(fmt.Sprintf("%s:%d", addr, port))
// Good
ep := netip.AddrPortFrom(addr, port)
- انباره کارآمد
// Bad
map[string]string // Using string representations
// Good
map[netip.AddrPort]interface{} // Using AddrPort directly
- عملیات دسته
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 است.