microg 是最贴近轻量级微服务治理平台定位的 Go 框架,开箱即用支持注册发现、gRPC/HTTP 双协议、链路追踪、结构化日志和错误码映射;但需注意 Consul 启动顺序与健康检查配置、熔断需外接 gobreaker、OTel traceID 跨协议透传需手动注入、zap 日志字段依赖上下文与初始化配置。

microg 是目前最贴近“轻量级微服务治理平台”定位的 Go 框架,它不堆砌功能,但把注册发现、gRPC/HTTP 双协议、链路追踪、结构化日志、错误码映射这些关键治理能力都做了开箱即用的封装。想快速落地一个能进生产的小型治理平台,直接基于它起步比从零搭轮子更稳妥。
consul 服务注册失败时 microg 启动卡住或静默退出
常见现象是服务没出现在 Consul UI,日志里也没有明显报错,app.Run() 调用后进程直接退出或 hang 住。
- 默认健康检查路径是
/health,但microg不自动注册该 endpoint —— 你得自己加一个 HTTP handler 或确保 gRPC server 实现了Check方法 -
consul.New()构造时若未显式传入consul.WithHealthCheck(true),即使 Consul 配置了健康检查,也不会生效 - Consul agent 必须运行在
127.0.0.1:8500(默认地址),否则要通过api.DefaultConfig().Address显式覆盖,不能只改环境变量 - 启动顺序必须是:先起 Consul,再起服务;如果 Consul 不可用,
microg默认不重试,会直接失败退出 —— 想容错需包装registrar加重试逻辑
gRPC 客户端调用超时却没触发熔断
你在服务 A 里用 microg 的 rpcclient 调用服务 B,B 假死或网络中断,A 的请求等满 5 秒超时后返回错误,但后续请求依然照常发过去,没有进入熔断状态。
-
microg本身不内置熔断器,它的rpcclient只负责连接管理与超时控制,熔断需额外集成sony/gobreaker或resilience-go - 别把熔断逻辑写在业务 handler 里 —— 应该封装到 client 层,对每个下游服务实例维护独立的
CircuitBreaker实例 - 注意
gobreaker的OnStateChange回调不会自动打日志,出问题时很难感知是否真的熔断了,建议加一行log.Info("circuit breaker state changed", "state", newState) - 熔断器配置中
ReadyToTrip函数判断的是“连续失败次数”,不是“单位时间失败率”,容易误判 —— 若下游偶发抖动,建议改成基于滑动窗口的失败率统计(需自行扩展)
OpenTelemetry traceID 在 HTTP 和 gRPC 之间丢失
前端发起 HTTP 请求,经 microg 的 REST server 进来,再通过 gRPC 调用下游服务,但下游日志里的 trace_id 是新生成的,跟上游不一致。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
microg的 HTTP server(基于 Gin)和 gRPC server 默认都启用了 OTel 中间件,但跨协议透传依赖W3C Trace Context标准头 —— 确保客户端发请求时带了traceparent头 - Gin 中间件注入的 context 是
*gin.Context,而 gRPC client 调用需要context.Context;必须手动从gin.Context.Request.Context()提取 span 并注入到 gRPC ctx,不能直接传c.Request.Context() - 如果你用
grpc-gateway做 REST-to-gRPC 转发,它默认透传traceparent,但microg自带的 REST server 不做这层转换 —— 需要在 handler 里显式调用otel.GetTextMapPropagator().Inject() - zap 日志字段里的
trace_id来自当前 goroutine 的 context,如果中间有 goroutine 切换(比如异步发消息),必须用ctx = context.WithValue(ctx, ...)显式传递,不能靠 goroutine 继承
zap 日志里看不到 service_name 或 span_id 字段
日志输出是纯 JSON,但关键字段如 service_name、span_id 缺失,导致日志无法关联到链路或服务维度。
立即学习“go语言免费学习笔记(深入)”;
-
microg的 zap logger 初始化时会尝试从 app context 读取serviceName,但如果你用app.WithName("user")是生效的;如果漏了这一步,service_name就是空字符串 -
span_id依赖 OpenTelemetry 的SpanContext注入,如果当前 goroutine 没在 span 里(比如在 init 函数或定时任务中打日志),就不会有该字段 —— 不是 bug,是预期行为 - zap 的
core配置里若用了zap.AddCaller(),可能干扰字段顺序,建议关掉或用zap.WrapCore()单独加字段 - 日志字段名是硬编码在
microg/pkg/log/zap.go里的,比如"trace_id",不能通过配置改 —— 如需定制,得 fork 后改源码

















