正确使用 go context.Context

Correct use of go context.Context

我刚刚阅读了这篇文章:Build You Own Web Framework In Go and for sharing values among handlers I picked the context.Context 并且我通过以下方式使用它在处理程序和中间件之间共享值:

type appContext struct {
    db     *sql.DB
    ctx    context.Context
    cancel context.CancelFunc
 }


func (c *appContext)authHandler(next http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request {
        defer c.cancel() //this feels weird
        authToken := r.Header.Get("Authorization") // this fakes a form
        c.ctx = getUser(c.ctx, c.db, authToken) // this also feels weird
        next.ServeHTTP(w, r)
    }

    return http.HandlerFunc(fn)
}

func (c *appContext)adminHandler(w http.ResponseWriter, r *http.Request) {
    defer c.cancel()
    user := c.ctx.Value(0).(user)
    json.NewEncoder(w).Encode(user)
}

func getUser(ctx context.Context, db *sql.DB, token string) context.Context{
    //this function mimics a database access
    return context.WithValue(ctx, 0, user{Nome:"Default user"})
}

func main() {
    db, err := sql.Open("my-driver", "my.db")
    if err != nil {
        panic(err)
    }
    ctx, cancel := context.WithCancel(context.Background())
    appC := appContext{db, ctx, cancel}
    //....
}

一切正常,处理程序加载速度比使用 gorilla/context 快,所以我的问题是:

  1. 这种方法安全吗?
  2. 真的有必要像我这样延迟 c.cancel() 函数吗?
  3. 我可以使用它来通过使用像结构这样的控制器与模型共享值来实现自定义 Web 框架吗?

您的代码有问题,因为您将用户存储到应用上下文中。同时有多个用户,它不起作用。上下文必须与不被其他请求覆盖的请求相关。用户必须存储在请求上下文中。在我的文章中,我使用以下大猩猩函数:context.Set(r, "user", user)r 是请求。

如果你想在你的应用程序中使用 context.Context,你应该使用他们的大猩猩包装器(你可以在本文末尾找到它:https://blog.golang.org/context)。

此外,您不需要取消上下文。 context.Background() 可以用于根上下文。

注意:go 1.7.0-rc2 确实澄清了一点 how to release resources associated with Contexts (Sameer Ajmani):

Some users don't realize that creating a Context with a CancelFunc attaches a subtree to the parent, and that that subtree is not released until the CancelFunc is called or the parent is canceled.

Make this clear early in the package docs, so that people learning about this package have the right conceptual model.

documentation now includes:

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context.
The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue.
These Context values form a tree: when a Context is canceled, all Contexts derived from it are also canceled.

The WithCancel, WithDeadline, and WithTimeout functions return a derived Context and a CancelFunc.
Calling the CancelFunc cancels the new Context and any Contexts derived from it, removes the Context from the parent's tree, and stops any associated timers.
Failing to call the CancelFunc leaks the associated resources until the parent Context is canceled or the timer fires.

Context 包的两个主要用例是:

  1. 用于存储请求范围内的值 - 使用 context.WithValue()
  2. 对于取消 - 使用 context.WithCancel()、context.WithTimeout()、context.WithDeadline()

上下文的使用形成了一个以context.Background()为根的上下文树。 WithValue() ,context.WithCancel(), WithTimeout(), WithDeadline() 是根上下文的派生上下文,可以进一步划分。每个示例都可以清楚地说明什么是正确的使用上下文。遇到了本指南,其中提供了上面讨论的所有内容的使用以及适当的示例。

来源:https://golangbyexample.com/using-context-in-golang-complete-guide/