You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

压测遇dial tcp: socket: too many open files错误,求ulimit外解决方案

Great question! The dial tcp: socket: too many open files error under load typically happens because your app is exhausting the OS's file descriptor limit—each open TCP socket counts as a file descriptor. Instead of tweaking ulimit, here are practical, code-focused fixes tailored to your Go implementation:

Fixes Without Modifying ulimit

1. Optimize HTTP Client Connection Reuse (Most Critical)

Your current code explicitly disables connection reuse with req.Close=true and Connection: close, which forces a new TCP socket for every request. This is a fast track to hitting file descriptor limits. Instead, configure the http.Client's Transport to use a connection pool, so sockets are reused across requests:

Update your HttpClient initialization:

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

var HttpClient = &http.Client{
    Transport: &http.Transport{
        // Global max idle connections across all hosts
        MaxIdleConns: 100,
        // Max idle connections per target host (prevents overwhelming one server)
        MaxIdleConnsPerHost: 20,
        // Timeout for idle connections to free up resources
        IdleConnTimeout: 90 * time.Second,
        // Max concurrent connections per host (caps new socket creation)
        MaxConnsPerHost: 20,
        // Disable HTTP/2 if you don't need it (reduces connection overhead)
        ForceAttemptHTTP2: false,
        // Tuned dialer for faster connection setup and keepalives
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
    },
}

Then remove these lines from your request setup—let the connection pool handle reuse automatically:

req.Header.Set("Connection", "close")
req.Close=true

2. Strictly Close Response Bodies (Prevent Connection Leaks)

Your code only closes the response body if the request succeeds with a 200 status. If the request fails (e.g., non-200 status, network error), the body might stay open, leaking connections and file descriptors. Use defer to guarantee cleanup, even if errors occur:

Revise your request handling code:

resp, err := HttpClient.Do(req)
if err != nil {
    fmt.Println(err)
    return // Handle error appropriately
}
// Always clean up the response body, no matter what
defer func() {
    // Read the entire body before closing to avoid blocking connection reuse
    io.Copy(ioutil.Discard, resp.Body)
    resp.Body.Close()
}()

if resp.StatusCode == 200 {
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    var Response Struct_Response
    err = json.Unmarshal(body, &Response)
    if err != nil {
        fmt.Println(err)
    }
}

Note: You need to read the full response body (even if you don't care about it) before closing—otherwise, the HTTP client won't return the connection to the pool.

3. Limit Concurrent Requests (Client-Side Throttling)

Even with a connection pool, a sudden flood of concurrent requests can create more sockets than the pool can handle. Use a semaphore to cap the number of simultaneous requests:

For batch request scenarios, add this to your main function:

import (
    "sync"
    "bytes"
)

func main() {
    maxConcurrentRequests := 50 // Adjust based on your server's capacity
    semaphore := make(chan struct{}, maxConcurrentRequests)
    var wg sync.WaitGroup

    totalRequests := 1000 // Example: number of requests to send
    wg.Add(totalRequests)

    for i := 0; i < totalRequests; i++ {
        semaphore <- struct{}{} // Acquire a slot
        go func() {
            defer func() {
                <-semaphore // Release the slot
                wg.Done()
            }()

            // Your request logic here (fixed the bytes.NewBuffer bug too!)
            apiUrl := "http://example.com"
            jsonStr := "teststr"
            connToken := "1233333333333"
            
            req, err := http.NewRequest("POST", apiUrl, bytes.NewBufferString(jsonStr))
            if err != nil {
                fmt.Println(err)
                return
            }
            req.Header.Set("Content-Type", "application/json")
            req.Header.Set("requestid", "1234")
            req.Header.Set("Authorization", "Bearer "+connToken)

            resp, err := HttpClient.Do(req)
            if err != nil {
                fmt.Println(err)
                return
            }
            defer func() {
                io.Copy(ioutil.Discard, resp.Body)
                resp.Body.Close()
            }()

            if resp.StatusCode == 200 {
                body, err := ioutil.ReadAll(resp.Body)
                if err != nil {
                    fmt.Println(err)
                    return
                }
                var Response Struct_Response
                err = json.Unmarshal(body, &Response)
                if err != nil {
                    fmt.Println(err)
                }
            }
        }()
    }
    wg.Wait()
}

Side note: Fixed bytes.NewBuffer(jsonStr) to bytes.NewBufferString(jsonStr)—the original would throw a type error since jsonStr is a string, not a byte slice.

4. Audit for Other File Descriptor Leaks

If you still hit the limit after the above fixes, check if your code opens other files (e.g., logs, local files) without closing them. Every open file consumes a descriptor, so ensure all os.File handles are closed with defer.


These changes should drastically reduce the number of open sockets your app uses, eliminating the need to adjust ulimit for most load scenarios.

内容的提问来源于stack exchange,提问作者Vani Polnedi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:10:49