Go微服务最常踩的坑是架构设计误判:服务边界应按业务域(如订单、用户、库存)而非技术层切分,避免跨服务DAO直连;慎用HTTP同步调用,强一致性场景配timeout与重试,最终一致性走消息队列;必须全程传递context控制超时与取消;可观测性(结构化日志、OpenTelemetry追踪、Prometheus指标)需从第一天集成。

Go微服务里最常踩的坑不是语法问题,而是架构设计层面的误判——一旦选错方向,后期重构成本远高于重写。
服务边界按技术层切分,导致耦合严重
把 handler、service、dao 拆成不同服务,是新手最典型的错误。这会让一个业务动作(比如创建订单)横跨 4–5 个服务调用,数据库事务、缓存一致性、错误回滚全变成分布式难题。
- 正确做法是按业务域建模:订单、用户、库存各自独立部署,每个服务拥有自己的数据库和 API 边界
- 禁止跨服务直接访问对方的 DAO 层或 SQL 表;需要数据时走明确的接口契约(gRPC 或 REST)
- DDD 的“有界上下文”不是理论概念,而是服务拆分的唯一依据——问自己:这个功能变更是否能独立发布?是否能独立扩缩容?
HTTP 同步调用滥用,引发级联超时
在订单服务里直接 http.Get("http://inventory-service/check"),看似简单,但高并发下极易触发雪崩:库存服务慢 1 秒 → 订单服务 goroutine 堆积 → 连接耗尽 → 整个链路瘫痪。
- 强一致性场景(如支付扣款)才用同步调用,且必须配
context.WithTimeout和重试退避 - 最终一致性场景(如发通知、更新统计)一律改用消息队列,
NATS或Kafka发布事件后立即返回 - 别在 handler 里裸写
go func() { ... }()—— 缺少context控制和并发限制,请求量一上来就 OOM
忽略 context 传递,请求链路失控
漏传 ctx 是静默杀手:上游超时了,下游还在查数据库;父请求取消了,子 goroutine 却永远卡在 db.Query 上。
立即学习“go语言免费学习笔记(深入)”;
- 所有 I/O 操作必须用带 ctx 的版本:
db.QueryContext(ctx, ...)、http.NewRequestWithContext(ctx, ...)、time.AfterFunc(ctx.Done(), ...) - 中间件、插件、自定义 client 的函数签名第一参数必须是
ctx context.Context - 绝对不用
context.Background()替代传入的ctx,否则整个链路脱离取消控制
可观测性从第一天就缺失
没集成日志、追踪、指标的服务,上线等于盲开——故障来了只能靠猜。
- 日志必须结构化:
zap输出 JSON,字段含trace_id、service_name、http_status - 链路追踪用
OpenTelemetry自动注入 span,不要手写 tracer - 关键指标暴露给
Prometheus:HTTP 请求延迟、goroutine 数、DB 连接池使用率 - 别等出问题再补——CI 流水线里加
go test -race,Docker 构建阶段校验go.sum
这些坑不靠经验很难避开,尤其当团队从单体迁移到微服务时,旧架构惯性会本能地把 Go 当成“更快的 Java”来用。真正难的不是写代码,是让每个服务都真正独立运转起来。


















