go不内置负载均衡,需依赖外部组件或手动实现;其http.Server仅负责本机请求分发,真实LB需结合consul/etcd等注册中心与nginx/Envoy等专用组件完成。

Go 本身不内置负载均衡,得靠外部组件或自己调度
Go 的 http.Server 是单进程多协程模型,能轻松扛住数千并发连接,但它只负责「本机内分发」——把请求交给 Handler,并不知道其他机器是否存在、健康与否。真正的负载均衡发生在服务之间,不是语言层面的事。所以别指望 net/http 自带轮询或一致性哈希。
用反向代理(httputil.NewSingleHostReverseproxy)做简易后端路由
适合开发联调或轻量级网关场景,但注意它默认不重试、不健康检查、不支持权重。你得手动封装逻辑:
- 用
sync.map或atomic.Value缓存后端列表,避免每次请求都查配置 - 在
Director函数里改写req.URL,比如轮询选backends[i%len(backends)] - 捕获
RoundTrip错误,对超时或连接拒绝做简单降级(如返回 503 或 fallback 到备用地址) - 别直接用
http.DefaultTransport,要自定义Timeout和MaxIdleConnsPerHost,否则容易耗尽文件描述符
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: "127.0.0.1:8081"}) proxy.Transport = &http.Transport{ DialContext: (&net.Dialer{ Timeout: 3 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConns: 100, MaxIdleConnsPerHost: 100, }
生产环境必须用专用 LB 组件,Go 只负责注册与心跳
真实高并发下,靠 Go 自己做负载均衡等于重复造轮子。主流做法是:Go 服务启动时向 Consul / Etcd / Nacos 注册自身地址 + 健康端点;由 Nginx / Envoy / traefik 或云厂商 SLB 来拉取节点、做健康探测、执行加权轮询或 least_conn。Go 这边只需:
- 暴露
/healthzHTTP 接口,返回 200 即视为存活(别做 DB 检查,太重) - 注册时带 TTL,定期刷新(如每 15s PUT 一次 Etcd key)
- 监听注销信号(
os.Interrupt),退出前主动删注册项 - 避免在
init()里做服务发现初始化,会阻塞main()启动
别忽略连接复用和上下文传递的细节
高并发下,每个请求新建 TCP 连接或 HTTP client 是性能杀手。常见错误包括:
立即学习“go语言免费学习笔记(深入)”;
- 在 handler 里 new
http.Client,导致大量 TIME_WAIT 和 dns 查询 - 没传
context.WithTimeout给下游调用,一个慢接口拖垮整条链路 - 用
http.DefaultClient但没调Transport超时参数,底层 TCP 握手失败可能卡 3 分钟 - 跨 goroutine 传递
context.Context时用了context.background()替代 handler 的req.Context(),导致无法取消
真正难的不是写个轮询函数,而是让每个环节都可观察、可中断、可退化——比如 LB 失效时自动切到本地兜底节点,或者健康检查连续失败三次才下线,这些逻辑藏在细节里,不在框架里。