kubernetes 中 livenessprobe 和 readinessprobe 必须分离:livenessprobe 仅检查进程存活与内存压力,避免误重启;readinessprobe 则必须真实验证 db、redis、grpc 等关键依赖,任一失败即返回 503 并从流量中剔除。

容灾不是“多部署几台机器”就能解决的事,核心在于故障发生时能否自动隔离、降级、切换,且不依赖人工干预。光靠 Kubernetes 的 Pod 重启或 Service 负载均衡远远不够——它不感知业务层依赖是否真实可用,也不处理级联失败。
/health/live 和 /health/ready 必须分离且语义明确
很多团队把两个探针都指向同一个 /health 接口,结果 readinessProbe 通过了,但 DB 连接池已耗尽,新流量进来直接超时堆积。Kubernetes 的 livenessProbe 应只检查进程存活和内存压力(比如 goroutine 数是否突增),而 readinessProbe 必须同步验证关键依赖:
-
/health/live:返回200即可,不做 DB/Redis/gRPC 检查,避免因短暂网络抖动触发容器重启 -
/health/ready:必须调用db.PingContext()、redis.Client.Ping(ctx).Err()、grpcConn.Invoke(...)等真实依赖操作,任一失败就返回503 - Gin 中别用
c.JSON(200, gin.H{"ok": true}),应返回结构体如{"db": "up", "redis": "down", "grpc_user": "timeout"},便于 Prometheus 抓取各依赖状态
gRPC 客户端连接池必须配超时、重试与 Keepalive
默认的 grpc.Dial 不启用重试,TCP 连接在丢包或 NAT 超时后不会自动重建,上游 goroutine 会卡在 ctx.Done() 直到超时,导致连接池耗尽。正确配置要点:
- 必须传
grpc.WithDefaultCallOptions(grpc.WaitForReady(true)),否则单次失败就放弃,无法利用连接池重试能力 - 用
grpc.WithKeepaliveParams(keepalive.KeepaliveParams{Time: 30 * time.Second, Timeout: 10 * time.Second})主动探测连接活性,防止假死 - 对非幂等调用(如 POST/PUT),禁止自动重试;对幂等 GET,用
grpc_retry库配合指数退避,最多 3 次,每次间隔time.Second+ 随机抖动 - 每个下游服务(user、order、payment)应持有独立的
*grpc.ClientConn,避免一个服务故障拖垮全部连接池
etcd 注册中心 TTL 与心跳间隔必须严格匹配
常见错误是设 LeaseGrant(5)(5 秒租约),但心跳间隔写成 8 秒 —— etcd 在租约到期前就会清理 key,导致服务反复上下线,客户端拿到的 endpoints 列表剧烈抖动。实操要点:
- 租约 TTL 至少为心跳间隔的 2 倍,推荐设为 15–30 秒,心跳间隔固定为 TTL/2(如 TTL=30s → 心跳每 15s 发一次)
- 必须使用流式续租(
lease.KeepAlive返回的chan *clientv3.LeaseKeepAliveResponse),而非轮询LeaseRenew,后者在网络延迟高时极易漏发 - 注册 key 路径要带唯一标识,如
/services/order/{hostname}_{pid},方便定位僵尸节点;退出前务必调用lease.Revoke,否则残留 key 会持续占用 lease quota
熔断器窗口期和半开探测不能省略
只初始化 sony/gobreaker.NewCircuitBreaker 而不设参数,等于没开熔断。错误率阈值、滑动窗口、半开探测间隔三者缺一不可:
- 窗口时间至少 30 秒(
Settings.Window: 30 * time.Second),太短会导致误判;错误率阈值建议 50%(Settings.ReadyToTrip) - 半开状态必须配置
Settings.OnHalfOpen回调,主动发起试探性调用,成功则恢复 CLOSED,失败则重置计数器并回到 OPEN - 熔断触发后,必须提供降级逻辑(如返回缓存数据、兜底 mock 响应),不能直接 panic 或返回空结构体
- 所有熔断事件需打点到 Prometheus:
gobreaker_state_total{service="user",state="open"} 1,否则无法关联故障根因
真正难的不是写熔断代码,而是定义“什么算失败”——比如 DB 查询超时是失败,但慢查询(95 分位 >2s)是否该计入?这需要结合业务 SLA 手动校准,没有通用阈值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











