「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
介绍
在许多 Go API,尤其是现代 API 中,函数和方法的第一个参数通常是 context.Context--上下文。 上下文提供了一种跨 API 边界和进程之间传输截止日期、调用者退出和其他请求链路的参数。 当库直接或传递地与远程服务器(例如数据库、API 等)交互时,通常会使用它。
这个文章给了一个建议:
Context不应该存储在结构类型中,而是传递给需要它的每个函数。
本文扩展了该建议的原因和示例,描述了为什么传递 Context 而不是将其存储在另一种类型中很重要。 它还强调了在结构类型中存储 Context极少数意义的罕见情况,以及如何安全地这样做。
优先选择参数传递的context
为了理解不将context存储在结构中的建议,让我们考虑将context作为参数的方法:
// Worker fetches and adds works to a remote work orchestration server.
type Worker struct { /* … */ }
type Work struct { /* … */ }
func New() *Worker {
return &Worker{}
}
func (w *Worker) Fetch(ctx context.Context) (*Work, error) {
_ = ctx // A per-call ctx is used for cancellation, deadlines, and metadata.
}
func (w *Worker) Process(ctx context.Context, work *Work) error {
_ = ctx // A per-call ctx is used for cancellation, deadlines, and metadata.
}
在这里, (Worker).Fetch 和 ( Worker).Process 方法都直接接受上下文。 通过这种参数传递的设计,用户可以设置每次调用的截止日期、调用取消和元数据。 而且,很清楚使用传递给每个方法的 context.Context会怎么使用:不期望传递给一个方法的 context.Context 将被任何其他方法使用。 这是因为上下文被限定为尽可能小的操作,这大大增加了这个包中上下文的实用性和清晰度。
在结构中存储context会导致混乱
让我们用不受欢迎的 context-in-struct 方法再次检查上面的 Worker 示例。 它的问题在于,当您将上下文存储在结构中时,您会模糊调用者的生命周期,更坏的是,以不可预知的方式将两个作用域混合在一起:
type Worker struct {
ctx context.Context
}
func New(ctx context.Context) *Worker {
return &Worker{ctx: ctx}
}
func (w *Worker) Fetch() (*Work, error) {
_ = w.ctx // A shared w.ctx is used for cancellation, deadlines, and metadata.
}
func (w *Worker) Process(work *Work) error {
_ = w.ctx // A shared w.ctx is used for cancellation, deadlines, and metadata.
}