go微服务不负责伸缩决策,只须暴露指标、响应信号、上报状态;真正扩缩容由k8s hpa/keda完成,全局限流由service mesh或网关实现。

Go 微服务本身不负责“伸缩决策”或“流量拦截”,它只负责暴露可被基础设施识别的状态、响应信号、上报指标;真正做扩缩容的是 Kubernetes HPA 或 KEDA,真正做全局限流的是 service mesh(如 Istio)或网关层。你在 Go 里要做的,是让服务不拖后腿。
为什么 /healthz 和 /readyz 必须分开实现
很多团队把两个端点写成同一个 handler,返回固定 200,结果扩容后大量请求打到还没连上数据库的实例上,503 率飙升。
-
/healthz只检查进程是否存活:能响应 HTTP、goroutine 没卡死、监听端口没被占 —— 不查 DB、Redis、配置中心 -
/readyz必须检查关键依赖:用db.PingContext()验证连接池可用性,redis.Ping()确认缓存连通,还要检查 config watcher 是否已加载初始配置 - Kubernetes 的
readinessProbe失败时,Pod 会从 Service 的 endpoints 列表中移除,但容器不会重启 —— 这是避免流量误导的关键机制 - 务必在
/readyzhandler 中加context.WithTimeout(r.Context(), 2*time.Second),防止依赖慢响应阻塞整个探针周期
优雅退出时 Shutdown() 为什么总超时失败
常见现象是 SIGTERM 发出后,srv.Shutdown() 卡住超过 10 秒,K8s 强制 kill,导致正在写的日志截断、事务未提交、gRPC 流中断。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 必须在
Shutdown()前手动关闭所有长生命周期资源:调用db.Close()、redis.Close()、停止grpc.Server.GracefulStop() - 避免在
http.Handler中启动无限for { select { ... } }goroutine 且不监听ctx.Done()—— 这类 goroutine 不会被Shutdown()等待,但会阻止进程退出 - 设置
srv.SetKeepAlivesEnabled(false),防止新连接在 Shutdown 过程中悄悄接入 - 不要在
init()或包级变量初始化里做耗时操作(如加载大配置文件、预热缓存),这些会拖慢 Pod 启动,间接拉长就绪时间
自定义指标上报给 HPA 的三个硬性前提
你写了 prometheus.NewCounterVec() 并注册了 /metrics,但 HPA 依然报 failed to get metric value —— 通常卡在这三处。
- Deployment 中必须设置
resources.requests(如cpu: 100m),否则 HPA 认为该 Pod 不可伸缩,直接跳过 -
/metrics端点不能加鉴权、不能走中间件限流、不能打日志(会影响采集性能),且响应体必须是纯文本 Prometheus 格式 - Prometheus-adapter 的规则配置必须和 HPA 的
metric.name完全一致;例如 HPA 查http_requests_total,adapter 就得映射rate(http_requests_total[2m]),少个下划线或大小写错误都会失败 - go-zero 用户注意:
prometheus.Enable()默认开启,但若你手动调用了prometheus.Register()注册了冲突 collector,会导致/metrics返回 500
本地限流只是兜底,别当主力用
有人在每个 HTTP handler 里套一层 tokenBucket.Limit(),以为能扛住洪峰 —— 实际上线后发现 CPU 暴涨、延迟翻倍,因为限流逻辑本身成了瓶颈。
- 单机限流(如
golang.org/x/time/rate.Limiter)只适合保护下游依赖(如 DB 连接池),不适合替代网关层的全局 QPS 控制 - 令牌桶的
burst值设太高(比如 >1000),会导致突发流量瞬间打满 goroutine 数,触发GOMAXPROCS扩容抖动 - 若真要用,建议放在中间件最外层,且用
context.WithTimeout()包裹,避免限流等待阻塞整个请求生命周期 - 更稳妥的做法是:网关层(如 Kong、APISIX)做全局速率控制,Go 服务只做连接数/队列长度等轻量级熔断(如
google.golang.org/grpc/codes.ResourceExhausted)
最常被忽略的一点:弹性伸缩生效的前提是「所有副本行为一致」。如果你的服务依赖本地磁盘写临时文件、用 sync.Map 缓存用户 session、或者靠随机数选主 —— 那么无论 HPA 扩多少个副本,系统整体都不可预测。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










