高可用容灾需超时、重试、熔断、健康检查、服务发现五环咬合;grpc必须配置超时、重试、keepalive,k8s健康检查须分离liveness与readiness并检测依赖。

高可用容灾不是加几个库就能成的事,而是超时、重试、熔断、健康检查、服务发现这五块必须咬合运转——漏掉任意一环,故障时大概率雪崩。
gRPC 调用必须配超时 + 重试 + keepalive
默认 grpc.Dial 不设超时、不重试、不保活,网络抖动一次就卡死 goroutine。Kubernetes 里常见“服务明明在跑,但请求全 503”,根源往往在这里。
-
grpc.WithTimeout(5 * time.Second)必须传给grpc.Dial,防止初始化卡死 - 单次调用必须用
context.WithTimeout控制边界,比如下游 SLA 是 200ms,这里设 150ms - 启用 keepalive:
grpc.WithKeepaliveParams(keepalive.ClientParameters{Time: 30 * time.Second, Timeout: 10 * time.Second}),否则 NAT 网关超时后首请求必失败 - 重试只对
status.Code() == codes.Unavailable或连接错误生效,且最多 3 次,用backoff.Retry带 jitter 的指数退避
/health/live 和 /health/ready 必须分离且带依赖检测
只返回 {"ok": true} 的 /ping 或裸 /health 在 Kubernetes 中等于自欺欺人——kubelet 会把 DB 已断连的服务标记为 Ready,流量照打不误。
-
/health/live只查进程和内存,响应要快( -
/health/ready必须同步校验:DB 连接池能否取连接、RedisPING是否在 100ms 内返回、关键下游 gRPC 服务是否可连通 - 返回结构体字段要具体,例如
{"db": "up", "redis": "down", "user-service": "timeout"},别用布尔值 - Kubernetes 的
initialDelaySeconds至少设为 10,避免服务还没连上 DB 就被探活失败
熔断器不能只 import,得配对指标才有效
用 sony/gobreaker 却只写默认配置,等同于没开——它默认不统计 context.Canceled,而你 90% 的超时错误都走这个路径。
- 必须显式包装错误:
if errors.Is(err, context.DeadlineExceeded) || status.Code(err) == codes.Unavailable才计入失败计数 - 触发条件建议:
MaxRequests: 10(窗口内最小请求数)、Interval: 5 * time.Second、Timeout: 60 * time.Second - 半开状态不要一成功就全量放行,用
go-breaker的OnStateChange钩子做渐进式试探 - 熔断期间降级逻辑必须无外部依赖——不能在降级里再查 Redis 或调 DB,预加载内存 map 或返回静态兜底数据
服务注册必须主动续租 + 带唯一标识
etcd 或 Consul 里 TTL 设短了、心跳漏发、key 路径硬编码,会导致服务列表频繁抖动,客户端持续往已下线节点发请求。
- 注册 key 路径必须含主机名和 PID,例如
/services/order/{hostname}_{pid},方便定位僵尸进程 - TTL 至少是心跳间隔的 2 倍,比如心跳 10 秒,TTL 得设 20 秒以上;用
lease.KeepAlive流式续租,别轮询LeaseRenew - 进程退出前务必调
lease.Revoke(),否则 etcd 里残留 lease 会阻塞新实例注册 - 客户端必须本地缓存服务列表,并支持“剔除不健康实例”——注册中心挂了也不能全链路不可用
最常被忽略的其实是状态协同:比如 /health/ready 返回 down,但服务注册中心没同步下线;或者熔断器跳闸了,负载均衡还在往这个节点转发——这些缝隙才是故障真正扩散的地方。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











