惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
Jina AI
Jina AI
雷峰网
雷峰网
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
美团技术团队
V
V2EX
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件
博客园 - Franky
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
Apple Machine Learning Research
Apple Machine Learning Research
量子位
IT之家
IT之家
人人都是产品经理
人人都是产品经理
博客园_首页
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

流动

山西之旅 | 流动 博客焕新,记录继续 | 流动 友链 | 流动 博客加速实践 广府古城一日游 我做了一个口播短视频二创工具:VideoRemaker Windows 下 Codex Cli 更新报 Move-Item is denied 错误的解决 Caddy 转发 GOST 报 TLS internal error 问题的排查与解决 对口型视频合成方案对比:Wav2Lip、VideoReTalking 与 MuseTalk 2026 音色克隆方案对比:IndexTTS-2、CosyVoice、GPT-SoVITS、Fish Speech、VoxCPM 部署与实测 AI Agent折腾记(OpenClaw / Hermes Agent) 我的2025年 大连之行 回家收麦 六一儿童节爬长城 Golang database/sql 数据库断线自动重连机制解析 清明踏春,爬山看海 购入小牛G400T电动车 北京的三月飞雪 wrenAI本地LLM模型部署 天津一日游 2024年终总结 停止使用staticfile.org服务 使用 ImageMagick 自动添加水印,保护图片版权 如何注册一个.sol域名 奥森公园半日游 昌平42公里骑行绿道打卡 十月一日爬慕田峪长城 当Hugo遇上AVIF,优化图片加载 博客被恶意镜像
Golang默认Http Client导致的cannot assign requested addres...
Liudon · 2025-04-29 · via 流动

问题表现

重现代码:

package main

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

func main() {

	client := &http.Client{
		Timeout: time.Duration(3) * time.Second,
	}

	for i := 0; i < 100; i++ {
		go func() {
			for {
				req, _ := http.NewRequest(http.MethodGet, "https://baidu.com", nil)

				rsp, err := client.Do(req)
				if err != nil {
					fmt.Println("request failed", err)
					continue
				}

				rsp.Body.Close()

				body, err := io.ReadAll(rsp.Body)
				if err != nil {
					fmt.Println("read body failed", err)
					continue
				}

				fmt.Println(string(body))
			}
		}()
	}

	select {}
}

启动后,随着请求越来越多,很快就出现了"cannot assign requested address"错误,服务器出现大量TIME_WAIT连接。

问题原因

net/http代码

type Client struct {
	// Transport specifies the mechanism by which individual
	// HTTP requests are made.
	// If nil, DefaultTransport is used.
	Transport RoundTripper

未配置Transport时,使用默认的DefaultTransport。

var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: defaultTransportDialContext(&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}),
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

这里指定了最大空闲连接为100,未指定单个host的最大空闲连接。

func (t *Transport) maxIdleConnsPerHost() int {
	if v := t.MaxIdleConnsPerHost; v != 0 {
		return v
	}
	return DefaultMaxIdleConnsPerHost
}

如果未配置MaxIdleConnsPerHost,则使用默认的DefaultMaxIdleConnsPerHost配置。

// DefaultMaxIdleConnsPerHost is the default value of [Transport]'s
// MaxIdleConnsPerHost.
const DefaultMaxIdleConnsPerHost = 2

这下清楚了:

100个协程,请求同一个地址,只能保留2个空闲连接,超出的请求完就会退出,产生一个TIME_WAIT;

然后再创建一个连接,请求完关闭,又产生一个TIME_WAIT,直至耗尽端口。

解决方案

创建Http.Client时,配置MaxIdleConnsPerHost即可。