Golang中HTTP客户端端口复用的实现方法

Golang中HTTP客户端端口复用的实现方法 是否有办法为HTTP请求复用某个特定端口?

或者限制使用的本地端口数量,例如100个?

2 回复

我已完成以下内容: 服务器:https://go.dev/play/p/02LFGS9p7OQ 客户端:https://go.dev/play/p/uEY2DvwkoiR

你可以运行这些代码进行测试,欢迎提出任何建议。

更多关于Golang中HTTP客户端端口复用的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中,可以通过自定义TransportDialContext来实现HTTP客户端的端口复用或限制本地端口数量。以下是两种实现方式:

1. 复用特定端口

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    // 创建自定义Transport
    transport := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            // 指定本地地址和端口
            localAddr, _ := net.ResolveTCPAddr("tcp", "192.168.1.100:8080")
            
            dialer := &net.Dialer{
                LocalAddr: localAddr,
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }
            return dialer.DialContext(ctx, network, addr)
        },
    }

    client := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }

    resp, err := client.Get("http://example.com")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Println("Status:", resp.Status)
}

2. 限制本地端口数量(100个端口池)

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "sync"
    "time"
)

type PortPool struct {
    ports   chan string
    basePort int
    maxPort  int
}

func NewPortPool(basePort, maxPort, poolSize int) *PortPool {
    pool := &PortPool{
        ports:   make(chan string, poolSize),
        basePort: basePort,
        maxPort:  maxPort,
    }
    
    // 初始化端口池
    for i := 0; i < poolSize; i++ {
        port := basePort + (i % (maxPort - basePort + 1))
        pool.ports <- fmt.Sprintf(":%d", port)
    }
    
    return pool
}

func (p *PortPool) GetPort() string {
    return <-p.ports
}

func (p *PortPool) ReturnPort(port string) {
    p.ports <- port
}

func main() {
    // 创建端口池:从10000到10099共100个端口
    portPool := NewPortPool(10000, 10099, 100)
    
    transport := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            // 从端口池获取端口
            port := portPool.GetPort()
            defer portPool.ReturnPort(port)
            
            localAddr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0"+port)
            
            dialer := &net.Dialer{
                LocalAddr: localAddr,
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }
            return dialer.DialContext(ctx, network, addr)
        },
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 100,
        IdleConnTimeout:     90 * time.Second,
    }

    client := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }

    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            
            resp, err := client.Get("http://httpbin.org/get")
            if err != nil {
                fmt.Printf("Request %d failed: %v\n", i, err)
                return
            }
            defer resp.Body.Close()
            
            fmt.Printf("Request %d completed with status: %s\n", i, resp.Status)
        }(i)
    }
    
    wg.Wait()
}

3. 使用连接池限制并发连接数

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "sync"
    "time"
)

func main() {
    // 限制最多使用100个不同的本地端口
    var mu sync.Mutex
    portCounter := 0
    maxPorts := 100
    basePort := 50000
    
    transport := &http.Transport{
        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            mu.Lock()
            port := basePort + (portCounter % maxPorts)
            portCounter++
            mu.Unlock()
            
            localAddr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port))
            
            dialer := &net.Dialer{
                LocalAddr: localAddr,
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
            }
            return dialer.DialContext(ctx, network, addr)
        },
        MaxConnsPerHost:     100,  // 限制每个主机的最大连接数
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 100,
    }

    client := &http.Client{
        Transport: transport,
    }

    // 并发测试
    var wg sync.WaitGroup
    for i := 0; i < 200; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            
            resp, err := client.Get(fmt.Sprintf("http://httpbin.org/delay/%d", id%3))
            if err != nil {
                fmt.Printf("Request %d failed: %v\n", id, err)
                return
            }
            defer resp.Body.Close()
            
            fmt.Printf("Request %d: %s\n", id, resp.Status)
        }(i)
    }
    
    wg.Wait()
}

这些实现方式通过自定义DialContext来控制HTTP客户端使用的本地端口。第一种方法固定使用特定端口,第二种方法使用端口池管理100个端口,第三种方法通过计数器循环使用100个端口范围。可以根据具体需求选择合适的实现方式。

回到顶部