必须明确区分注册、选节点和健康剔除三环节——只做其中一两个,线上扩缩容或故障时立刻掉链子;consul+go-micro/v2是最快落地组合,支持开箱即用健康检查与dns;grpc round_robin不集成服务发现,需配合resolver动态更新地址列表,且无内置健康剔除,须自研熔断逻辑。

必须明确区分注册、选节点和健康剔除三环节——只做其中一两个,线上扩缩容或故障时立刻掉链子。
服务注册与发现:Consul + go-micro/v2 是最快落地的组合
Consul 提供开箱即用的健康检查、KV 存储和 DNS 接口;go-micro/v2 的 registry 和 selector 模块把发现和策略封装成可插拔组件,不依赖 Sidecar,所有逻辑都在 Go 进程内完成。
-
micro.NewService()初始化时传入consul.NewRegistry(),服务启动后自动注册到localhost:8500 - 客户端调用
service.Client().Call()时,selector默认走轮询(RoundRobin),也可显式指定selector.WithStrategy(selector.Random) - Consul Agent 必须可访问,且服务名必须全小写(
user-srv合法,UserSrv会被忽略) -
go-micro/v2的Registry接口不兼容 etcd v3 的 gRPC API;若强行换 etcd,得自己实现适配层
gRPC round_robin 策略:它不拉注册中心,只负责从已知地址中选一个
grpc-go 的 round_robin 本身不集成服务发现,你不能直接传 "10.0.1.5:8080" 这种静态地址,而必须让它能“解析”出动态列表。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- Kubernetes 环境下,配合 Headless Service(
clusterIP: None)+ DNS,让"dns:///user-svc.default.svc.cluster.local"解析出全部 Pod A 记录 - 非 K8s 环境下,需手写
resolver.Builder,监听 Consul/etcd 路径变化,调用cc.UpdateState()注入新地址列表 -
round_robin是连接粒度的:每个grpc.ClientConn内部维护自己的地址池,不会跨连接共享状态 - 没有内置健康剔除——如果某个实例宕机,
round_robin仍可能选中它,需配合 Keepalive 或自定义拦截器做失败熔断
自己实现 Balancer:三个坑防不住就卡在高并发上
很多团队最终会自己封装 RoundRobinBalancer 或 WeightedRandomBalancer,因为要加权重、平滑上线、灰度路由等定制逻辑,但细节容易翻车。
- 计数器必须用
atomic.Int64,不能用普通int+sync.Mutex——高并发下锁竞争会导致吞吐骤降 - 实例列表更新时,必须原子替换整个切片(
b.instances.Store(newList)),不能边遍历边修改底层数组 - 权重字段若来自注册中心元数据(如
weight=50),每次更新需重新归一化计算,否则加权结果失真
真正难的不是写个轮询逻辑,而是让注册、选节点、健康剔除三者在不同网络延迟、实例震荡、配置变更场景下保持行为一致——比如 Consul 健康检查间隔是 10 秒,而你的 Balancer 每 2 秒刷新一次列表,中间那 8 秒就可能把已宕机实例当活跃节点用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










