grpc与kafka在go微服务中分工明确:grpc负责低延迟同步调用,kafka负责解耦异步事件;服务发现须用etcd/consul而非dns;kafka生产者需启用幂等性与acks=all;grpc server与kafka consumer须分离goroutine运行。

gRPC 和 Kafka 在 Go 微服务集群中不是二选一,而是分工明确:gRPC 负责服务间低延迟、强契约的同步调用(比如订单创建后立刻查库存),Kafka 负责解耦、持久化、重试的异步事件分发(比如订单成功后发通知、写日志、触发风控)。强行把所有通信塞进 gRPC 或全扔进 Kafka,都会在稳定性或响应性上出问题。
gRPC 服务注册与发现必须绕过 DNS 直连
集群里多个 user-service 实例启动后,order-service 不能靠 user-service:50051 这种静态域名直连——DNS 缓存、无健康检查、不支持负载均衡。实际要用 etcd 或 consul 做服务注册,客户端通过 grpc.WithResolvers 集成对应 resolver。
-
etcd写入路径示例:/services/user-service/instance-001,值为{"addr":"10.0.2.15:50051","weight":100} - resolver 必须实现
Build、ResolveNow、Close三个方法,否则 gRPC 连接池不会刷新实例列表 - 别用
dns:///user-service:50051模式跑生产环境,它默认不重试失败节点,故障时直接报UNAVAILABLE
Kafka 生产者必须启用 idempotence + acks=all
Go 客户端(如 segmentio/kafka-go)默认不保证消息不丢、不重。集群部署下,Broker 故障或网络分区时,acks=1 或 acks=0 会导致消息静默丢失;没开幂等性则可能因重试造成重复。
- 关键配置必须显式设为:
"enable.idempotence": "true"、"acks": "all"、"retries": 20 - 不要依赖
producer.Close()等待未发送消息——它只等缓冲区 flush,不等 broker 确认;要用producer.Flush()+ context 超时控制 - Topic 分区数建议 ≥ 消费者组内最大并发消费者数,否则部分消费者会空转
gRPC Server 和 Kafka Consumer 不能共用一个 Goroutine 池
gRPC Server 默认用 runtime.GOMAXPROCS 控制并发,而 Kafka consumer 的 ReadMessage 是阻塞调用。如果把 consumer 放进 gRPC server 的 http.Server.Handler 或拦截器里,一旦 Kafka broker 延迟升高,整个 gRPC 请求链路会被卡住。
- consumer 应该作为独立 goroutine 启动,用
sync.WaitGroup管理生命周期 - 消费逻辑里避免调用 gRPC client,尤其不能同步调用同集群内其他服务——这会形成循环依赖和雪崩风险
- 若需回调,应发到另一个 Kafka topic,由下游服务异步处理,而非直接
client.Call()
真正难的不是写通 gRPC 和 Kafka 的代码,而是厘清哪条路径必须同步返回(比如支付扣款),哪条必须异步保底(比如发短信)。这个边界一旦模糊,集群扩到 20+ 服务后,超时、重试、死信堆积就会变成常态。











