不用框架也能搭分布式系统,但必须满足服务间通信、节点发现、错误传播三大硬约束;gRPC+Protobuf是通信最低可行方案,etcd是轻量级服务发现最优选,需配合Watch监听、合理Lease TTL及goroutine上下文管控。

直接上结论:不用框架也能搭,但必须先明确三个硬约束——服务间通信方式、节点发现机制、错误传播边界。否则集群会变成“伪分布式”,表面多个进程,实际单点故障频发、超时不可控、扩缩容形同虚设。
gRPC + Protocol Buffers 是服务间通信的最低可行选择
HTTP/REST看着简单,但在微服务集群里容易埋坑:序列化开销大、连接复用难控制、流式响应支持弱。gRPC天然适配Go的并发模型,且Protobuf定义强制接口契约。
-
proto文件必须显式声明package和service名称,否则Go生成代码时无法正确映射到模块路径 - 客户端调用前务必设置
WithTimeout和WithBlock,否则context.Background()会导致连接卡死在DNS解析阶段 - 不要在
protomessage里嵌套过深的结构体,Go生成的struct默认不支持json.Marshal,调试时日志打不出字段值
etcd 是轻量级服务发现最稳的落地选项
Consul功能全但二进制体积大、依赖多;ZooKeeper Java栈太重;而etcd用Go写、API简洁、Raft协议开箱即用,和微服务集群节奏一致。
- 注册服务时,
lease.TTL建议设为15秒,KeepAlive心跳间隔设为5秒——太短易误判下线,太长导致故障感知延迟 - 客户端做服务发现,不能只查一次
Get就缓存结果,必须监听Watch事件,否则节点宕机后请求仍会发往已失效地址 - etcd key路径要带版本号,比如
/services/user/v1,避免升级时新旧服务混跑引发协议不兼容
goroutine泄漏是集群静默崩溃的头号原因
每个HTTP handler或gRPC handler里启的goroutine,如果没配context或没处理panic,会持续占用内存和goroutine调度资源,几小时后整个节点OOM。
- 所有
go func()必须绑定父context,用ctx.Done()做退出信号,而不是靠time.Sleep硬等 - 数据库查询、HTTP调用、gRPC调用全部要包在
select里监听ctx.Done(),否则超时后goroutine还在跑 - 日志打印别用
log.Printf,改用zap.Sugar().Infof,否则高并发下日志I/O锁会拖垮整个goroutine调度器
真正麻烦的不是怎么把服务跑起来,而是当一个Data Node挂掉时,Coordinator怎么在200ms内切走流量、怎么让下游不感知、怎么避免重试风暴压垮隔壁服务——这些细节不在任何模板里,得靠grpc.UnaryInterceptor和etcd.Watcher组合出的熔断逻辑来填。

















