Golang中HTTP请求缓慢的优化方法
Golang中HTTP请求缓慢的优化方法 我目前正在开发的应用中有一个网络爬虫组件,它会访问一系列我付费使用的API端点。
整体运行良好,但不知为何,我发送的请求(使用 http 包)有时会变得非常缓慢,最糟糕时每个请求大约需要3分钟,但通常也需要长达3秒(而快速请求在理想情况下通常只需50毫秒左右)。
我想知道你们是否有人之前遇到过类似情况,或者知道可能是什么原因导致的?
补充信息:
- 所有请求都是顺序执行的,从未同时进行超过1个请求
- 内存使用量始终稳定在7MB左右
- pprof分析没有显示任何异常
提前感谢。
你是否在使用其他HTTP客户端(如curl)时观察到相同的行为?
更多关于Golang中HTTP请求缓慢的优化方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我怀疑是端点过载或网络问题,而不是HTTP客户端的问题。如果你需要在某些时候加速客户端,可以考虑使用fasthttp(尽管不支持http2)。
感谢大家的建议。
我采纳了建议,尝试了一个更简化的代码版本,速度确实更快了。这说明很可能是我原始代码中存在某些导致内存使用过高或拖慢HTTP请求的因素。
最终我通过同时运行10个并行协程来处理复杂代码,成功提升了整体速度。虽然单个请求的响应时间仍然不太理想,但通过增加并发数量弥补了这个缺陷。
你好。请问这些端点是否有针对DDoS攻击或类似威胁的防护措施?我们目前使用的网络服务需要降低访问频率。如果我们试图以最快速度获取响应,速度会变得越来越慢;但如果稍作等待,就能恢复正常。你可以采取以下几种措施:
- 联系API提供商,询问允许的请求频率限制
- 测试请求响应,观察是否存在规律——是随时间推移变慢,还是特定请求类型较慢
- 在每次请求间加入暂停间隔
从你的描述来看,问题很可能出现在网络层面或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)
}
这些优化主要针对网络层面的延迟问题。建议先实现超时设置和连接池配置,这通常能解决大部分偶发的慢请求问题。

