endpoint 必须是纯数据结构,使用 ValidateRequest/ValidateResponse 等业务字段结构体,函数签名严格为 func(context.Context, interface{}) (interface{}, error);transport 层负责编解码与协议适配,中间件应作用于 endpoint 而非 transport。

endpoint 必须是纯数据结构,不能带 net/http 依赖
直接用 http.Request 或 http.ResponseWriter 当参数或返回值,会导致 endpoint 失去协议无关性——它就只能跑在 HTTP 上,没法复用到 gRPC、CLI 或测试里。
正确做法是定义两个独立结构体:ValidateRequest 和 ValidateResponse,字段只含业务字段(比如 Date string),不引用任何标准库 HTTP 类型。
endpoint 函数签名必须严格为:func(context.Context, interface{}) (interface{}, error)。传入的 interface{} 由 transport 层负责 cast 成你的具体 request 结构;返回的 interface{} 同理由 transport 序列化。
transport/http.NewServer 不是“启动服务”,只是构造 handler
httptransport.NewServer 返回的是一个 http.Handler,不是监听器,也不开端口。常见错误是写了它就以为服务跑起来了,结果 curl 直接返回 404 或 connection refused。
你必须显式做三件事:
- 提供 decodeRequest 函数,把 *http.Request 解包成你的 ValidateRequest
- 提供 encodeResponse 函数,把 ValidateResponse 写回 http.ResponseWriter
- 把生成的 handler 注册到 http.ServeMux,再传给 http.ListenAndServe;如果 mux 是自己 new 的,就不能传 nil 给 ListenAndServe。
服务注册 Consul 前,先确认健康检查能通
go-kit 服务注册 Consul 失败时通常静默——进程没报错,但服务在 Consul UI 里看不到,其他服务也发现不了它。根本原因往往不在注册代码本身,而在健康检查配置:
- consul.Client 初始化时没设超时,网络抖动就卡住
- 健康检查路径(如 /health)没对应 handler,或 handler 返回非 200 状态码
- 注册时机太早:HTTP server 还没真正 listen,Consul 就已发起健康探测,必然失败
建议把注册逻辑放在 http.ListenAndServe 之后,用 goroutine 异步注册,并加 1–2 秒延迟确保端口已就绪。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
中间件要 wrap 在 endpoint 层,别塞进 transport
日志、熔断、限流这些中间件,必须作用于 endpoint,而不是 HTTP handler。因为 endpoint 是业务逻辑入口,所有协议(HTTP/gRPC/Thrift)都会经过它;而 transport 层只管序列化和网络,加中间件等于只对 HTTP 生效。
典型写法是:endpoint = loggingMiddleware(logger)(endpoint),再把包装后的 endpoint 传给 httptransport.NewServer。
如果误把中间件套在 handler 上(比如用 mux.Use()),gRPC 调用就完全绕过,监控指标漏采、熔断不生效——这种问题线上很难排查。
立即学习“go语言免费学习笔记(深入)”;
真实项目里最常被跳过的,是 decode/encode 函数的健壮性处理:比如没校验 JSON 字段缺失、没处理空 body、没统一 error 格式。这些细节不显眼,但一到压测或上游乱传数据,服务就 500 或 panic,且日志里只显示 “invalid character” 这类模糊提示。

















