Golang中HTTP请求缓慢的优化方法

Golang中HTTP请求缓慢的优化方法 我目前正在开发的应用中有一个网络爬虫组件,它会访问一系列我付费使用的API端点。

整体运行良好,但不知为何,我发送的请求(使用 http 包)有时会变得非常缓慢,最糟糕时每个请求大约需要3分钟,但通常也需要长达3秒(而快速请求在理想情况下通常只需50毫秒左右)。

我想知道你们是否有人之前遇到过类似情况,或者知道可能是什么原因导致的?

补充信息:

  • 所有请求都是顺序执行的,从未同时进行超过1个请求
  • 内存使用量始终稳定在7MB左右
  • pprof分析没有显示任何异常

提前感谢。

5 回复

你是否在使用其他HTTP客户端(如curl)时观察到相同的行为?

更多关于Golang中HTTP请求缓慢的优化方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我怀疑是端点过载或网络问题,而不是HTTP客户端的问题。如果你需要在某些时候加速客户端,可以考虑使用fasthttp(尽管不支持http2)。

另请查看 https://blog.golang.org/http-tracing

感谢大家的建议。

我采纳了建议,尝试了一个更简化的代码版本,速度确实更快了。这说明很可能是我原始代码中存在某些导致内存使用过高或拖慢HTTP请求的因素。

最终我通过同时运行10个并行协程来处理复杂代码,成功提升了整体速度。虽然单个请求的响应时间仍然不太理想,但通过增加并发数量弥补了这个缺陷。

你好。请问这些端点是否有针对DDoS攻击或类似威胁的防护措施?我们目前使用的网络服务需要降低访问频率。如果我们试图以最快速度获取响应,速度会变得越来越慢;但如果稍作等待,就能恢复正常。你可以采取以下几种措施:

  1. 联系API提供商,询问允许的请求频率限制
  2. 测试请求响应,观察是否存在规律——是随时间推移变慢,还是特定请求类型较慢
  3. 在每次请求间加入暂停间隔

从你的描述来看,问题很可能出现在网络层面或HTTP客户端配置上。以下是几个常见的优化方向:

1. 连接池和超时设置

默认的HTTP客户端没有设置超时,这会导致慢速请求阻塞整个应用:

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

// 优化后的HTTP客户端
client := &http.Client{
    Timeout: 30 * time.Second, // 总超时时间
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   5 * time.Second,  // 连接建立超时
            KeepAlive: 30 * time.Second, // 保持连接时间
        }).DialContext,
        TLSHandshakeTimeout: 5 * time.Second,    // TLS握手超时
        ResponseHeaderTimeout: 10 * time.Second, // 响应头超时
        ExpectContinueTimeout: 1 * time.Second,  // 100-continue超时
        MaxIdleConns:        100,                // 最大空闲连接数
        MaxIdleConnsPerHost: 10,                 // 每个主机最大空闲连接数
        IdleConnTimeout:     90 * time.Second,   // 空闲连接超时
    },
}

2. 启用HTTP/2和连接复用

import "golang.org/x/net/http2"

transport := &http.Transport{
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     90 * time.Second,
}

// 强制启用HTTP/2
http2.ConfigureTransport(transport)

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

3. DNS缓存优化

DNS查询延迟可能导致请求变慢:

import (
    "net"
    "time"
    "golang.org/x/net/idna"
)

// 自定义DNS解析器
resolver := &net.Resolver{
    PreferGo: true,
    Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
        d := net.Dialer{
            Timeout: 2 * time.Second,
        }
        return d.DialContext(ctx, "udp", "8.8.8.8:53")
    },
}

// 在Transport中使用自定义解析器
transport := &http.Transport{
    DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
        host, port, err := net.SplitHostPort(addr)
        if err != nil {
            return nil, err
        }
        
        // 使用自定义解析器
        ips, err := resolver.LookupIPAddr(ctx, host)
        if err != nil {
            return nil, err
        }
        
        // 选择第一个IP地址
        if len(ips) == 0 {
            return nil, fmt.Errorf("no IPs found for %s", host)
        }
        
        return net.Dial(network, net.JoinHostPort(ips[0].String(), port))
    },
}

4. 添加重试机制

对于偶发的网络问题,实现指数退避重试:

import (
    "math"
    "time"
)

func doRequestWithRetry(client *http.Client, req *http.Request, maxRetries int) (*http.Response, error) {
    var resp *http.Response
    var err error
    
    for i := 0; i <= maxRetries; i++ {
        resp, err = client.Do(req)
        if err == nil && resp.StatusCode < 500 {
            return resp, nil
        }
        
        if i < maxRetries {
            // 指数退避
            backoff := time.Duration(math.Pow(2, float64(i))) * time.Second
            time.Sleep(backoff)
        }
    }
    
    return resp, err
}

5. 完整的优化示例

package main

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

func createOptimizedClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
            TLSHandshakeTimeout: 5 * time.Second,
            ResponseHeaderTimeout: 10 * time.Second,
        },
    }
}

func makeRequest(client *http.Client, url string) (*http.Response, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return nil, err
    }
    
    return client.Do(req)
}

这些优化主要针对网络层面的延迟问题。建议先实现超时设置和连接池配置,这通常能解决大部分偶发的慢请求问题。

回到顶部