Go-Kit三层是职责分离抽象而非强制目录结构;Service层须定义纯领域接口,Endpoint层封装请求响应结构体并实现endpoint.Endpoint签名,Transport层需显式组装HTTP服务器并注册带编解码逻辑的Server。

Go-Kit 的三层不是强制目录结构,而是职责分离的抽象层;写错层级耦合或跳过 transport 组装,服务就跑不起来。
Service 层必须定义纯接口,不能带 transport 依赖
Service 是业务逻辑的唯一出口,它的接口必须只包含领域方法,比如 GetUser(id string) (User, error)。常见错误是把 http.ResponseWriter 或 context.Context 当作参数塞进接口——这会让 Service 无法被 gRPC 或 CLI 复用。
- 接口定义放在
service/目录下,如UserService,不 import 任何 transport 包 - 实现结构体可带依赖(如
*sql.DB或UserRepository),但方法签名必须严格匹配接口 - 不要在 Service 方法里做 JSON 序列化、HTTP 状态码设置、header 写入等操作
Endpoint 层要封装 request/response 结构体,不是 handler 函数
endpoint.Endpoint 是一个固定签名的函数:func(context.Context, interface{}) (interface{}, error)。它不处理网络,只做“业务调用 + 错误包装”。你写成 http.HandlerFunc 或直接调用 json.Marshal 就废了跨协议能力。
- request 和 response 必须是 plain struct,例如
type GetUserRequest { ID string },字段带json:tag - Endpoint 函数体内只做类型断言、调用 Service、构造响应结构体,不碰
net/http - 中间件(如日志、限流)必须作用于
endpoint.Endpoint类型,而不是 HTTP handler
Transport 层必须显式组装 http.NewServer,并注册到 mux
只写 MakeGetUserEndpoint(svc) 不会启动任何服务。Go-Kit 不自动监听端口,也不接管路由。你必须手动用 httptransport.NewServer 包裹 endpoint,并提供 decodeRequest 和 encodeResponse 函数,再注册到 http.ServeMux。
-
decodeRequest负责从*http.Request解出你的GetUserRequest,通常用json.NewDecoder(r.Body).Decode(&req) -
encodeResponse负责把GetUserResponse写回http.ResponseWriter,记得设w.Header().Set("Content-Type", "application/json") - 别用
http.ListenAndServe(":8080", nil)—— 如果你用了自定义http.NewServeMux(),必须传进去,否则路由失效
容易被忽略的关键点:中间件嵌套顺序和 context 传递
Endpoint 中间件是链式调用的,mw1(mw2(endpoint)) 表示 mw1 在外层、mw2 在内层。日志中间件如果放在熔断器之后,就可能记录不到被熔断的请求;而所有中间件都必须透传 ctx,否则 tracing 或 timeout 会断掉。
- 不要在中间件里覆盖
ctx后不返回,例如ctx = context.WithValue(...)后忘了return next(ctx, req) - transport 层的中间件(如 CORS)和 endpoint 层的中间件(如 auth)解决不同问题,不能混用
- 健康检查路径(如
/health)必须绕过业务 endpoint,直接走独立 handler,否则会被日志/限流干扰


















