go装饰器需手动函数包装或接口嵌入,核心是func(context.context) error签名,强调ctx取消、defer cancel、显式调用next.servehttp、避免panic吞没、泛型优于反射、严控context生命周期。

Go 没有装饰器语法,所有“装饰”都是手动函数包装或接口嵌入,写错签名、漏调 next.ServeHTTP 或忘了 defer cancel(),功能就直接失效。
func(context.Context) error 类型的高阶函数最常用
日志、超时、重试、限流这些横切逻辑,基本都围绕这个签名展开。它够通用,又能天然透传取消信号和超时控制。
- 必须保持输入输出类型完全一致,否则无法赋值给同名变量或传入期望该类型的参数位置
- 多个装饰器叠加时顺序敏感:
withTimeout(withRetry(f))表示先重试、再整体超时;反过来就变成每次重试都带独立timeout - 别在闭包里捕获
panic后只打日志不re-panic,否则上层收不到错误,HTTP handler 可能静默返回 200 - 超时包装器要
defer cancel(),且原函数内部也得响应ctx.Done(),否则 goroutine 泄漏
http.Handler 中间件必须显式调用 next.ServeHTTP
Go 标准库的 func(http.Handler) http.Handler 模式,就是装饰器在接口层面的落地。每个中间件接收一个 http.Handler,返回一个新的 http.Handler,靠结构体嵌入 + 方法重写实现。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 关键点是包装体里必须显式调用
h.next.ServeHTTP(w, r),漏掉这句,请求就卡死在当前中间件 - 避免循环引用:不要在包装结构体里存自己的指针,比如
type authHandler struct { next *authHandler }是危险的 - 转换普通函数为
http.Handler时,要用http.HandlerFunc(f)显式转换,不能直接传 - 如果想链式组合多个中间件,推荐用
chi等成熟路由库的Use方法,而不是手写嵌套调用
泛型包装器比反射更安全、更高效
有人想“一次写完适配所有函数签名”,于是用 reflect.MakeFunc。但真实项目里几乎没人这么干。
- 反射丢失编译期类型检查,IDE 跳不到原函数,
go vet和静态分析全失效 - 性能差一到两个数量级,高频调用(如每秒万级请求)下日志装饰器本身就能拖垮服务
- 真正可维护的做法是按常用签名写几个泛型版本,比如:
WithLog[T any](f func() (T, error))、WithLogCtx[T any](f func(context.Context) (T, error)) - 泛型方案既保类型安全,又免重复造轮子,还能让编译器帮你推导参数
context 传递和 cancel 管理最容易被忽略
几乎所有生产级装饰器都涉及 context,但很多人只记得传进去,却忘了它是个资源。
-
context.WithTimeout返回的cancel函数必须defer,否则超时前panic就泄漏context - 装饰器内部若启动 goroutine,必须确保其能感知
ctx.Done()并退出,否则泄漏的是整个 goroutine 树 - 不要在装饰器闭包里长期持有非线程安全的配置对象,尤其当该装饰器被注册为全局中间件时
最复杂的不是怎么写,而是每个装饰器都要自己处理 context 生命周期、错误传播路径、以及是否允许中断链路——这些细节不会报错,但会在压测或上线后悄悄吃掉稳定性。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










