http.head 更合适,它只取响应头、不拉正文,速度快开销小;但需处理 405 错误并 fallback 到 http.get,同时注意关闭 body、控制并发、配置超时与 transport、规范 url 解析及细化错误分类。

用 http.Head 还是 http.Get 做健康检查?
http.Head 更合适,它只取响应头,不拉正文,速度快、开销小,适合纯状态判断。但要注意:有些服务会禁用 HEAD 方法,返回 405 Method Not Allowed —— 这不代表网站离线,只是接口策略限制。
- 如果目标站点明确支持
HEAD(比如多数静态托管、CDN、标准 Web 服务),优先用http.Head - 遇到
405或超时后,可 fallback 到http.Get并立即resp.Body.Close(),避免连接堆积 - 别用
http.Get后不关Body,Golang 的http.Client会复用连接,不关会导致连接池耗尽、后续请求卡住
并发控制不当直接拖垮本地机器或目标服务器
Golang 的 goroutine 很轻量,但没节制地开几千个,反而会让 DNS 解析、文件描述符、TCP 连接数全爆掉。默认 http.DefaultClient 的 Transport 没设限,容易触发 dial tcp: lookup xxx: no such host 或 too many open files。
- 用
semaphore或带缓冲的 channel 控制并发数,建议初始值设为 20–50(视目标域名数量和网络环境调整) - 给
http.Client显式配置Timeout和Transport:client := &http.Client{ Timeout: 10 * time.Second, Transport: &http.Transport{ MaxIdleConns: 100, MaxIdleConnsPerHost: 100, IdleConnTimeout: 30 * time.Second, }, } - 不要共用一个
http.Client实例却在不同 goroutine 里改它的Timeout字段——它是非线程安全的
URL 格式不规范导致 net/url.Parse 失败或误判
用户输入的 URL 可能缺 https://、带空格、末尾多斜杠、含中文参数……直接丢给 http.Get 会 panic 或静默失败。
- 先用
strings.TrimSpace清空首尾空白 - 检查是否含 scheme:若不以
http://或https://开头,自动补https://(别补http://,现在多数站会 301 跳转,多一次往返) - 用
url.Parse解析后校验u.Scheme != "" && u.Host != "",否则跳过并记录警告 - 别用正则“自己造轮子”验 URL,
net/url已足够健壮;但注意它不校验域名是否存在,只做语法解析
怎么让失败结果真正“可读”而不是只打个 error 字符串?
err.Error() 常是 “Get https://www.php.cn/link/89188e01d32520f2c127da5a731796da: dial tcp: i/o timeout”,看不出是 DNS 问题、连不上、还是对方关了 HTTPS。得拆开看底层原因。
立即学习“go语言免费学习笔记(深入)”;
- 类型断言判断错误来源:
if netErr, ok := err.(net.Error); ok && netErr.Timeout()→ 超时;if urlErr, ok := err.(<em>url.Error); ok && urlErr.Err != nil</em>→ 看urlErr.Err是net.OpError还是*net.DNSError - 对
http.Response.StatusCode要分类:小于 400 算“在线”,4xx 算“业务异常”(如 404 表示路径不存在但服务活着),5xx 算“服务端故障” - 记录日志时带上原始 URL、耗时、状态码、错误类型,别只记
err,否则排查时得重跑一遍
真实场景里,最常被忽略的是 Transport 层连接复用和 DNS 缓存行为——同一 Client 对多个域名并发请求时,DNS 查询可能被阻塞,导致一批请求集体延迟。这不是代码写错了,而是没意识到 Go 默认的 DNS 解析器是同步阻塞的。










