3

我刚刚阅读了这篇文章:在 Go 中构建您自己的 Web 框架并在处理程序之间共享值,我选择了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. 我可以使用 struct 之类的控制器与模型共享值来使用它来实现自定义 Web 框架吗?
4

3 回答 3

1

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

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

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

于 2015-06-19T05:02:35.010 回答
1

注意:go 1.7.0-rc2 确实阐明了如何释放与上下文相关的资源Sameer Ajmani):

一些用户没有意识到Context使用 a创建CancelFunc一个子树会将一个子树附加到父级,并且该子树在CancelFunc被调用或父级被取消之前不会被释放。

在包文档中尽早说明这一点,以便了解此包的人拥有正确的概念模型。

文档现在包括

对服务器的传入请求应该创建一个Context,而对服务器的传出调用应该接受一个Context.
它们之间的函数调用链必须传播Context,可选择将其替换为使用、、或Context创建的派生函数。这些值形成一棵树:当 a被取消时,所有从它派生的也被取消WithCancelWithDeadlineWithTimeoutWithValue
ContextContextContexts

WithCancel和函数返回一个派生的WithDeadline和一个。 调用取消新的和从它派生的任何上下文,从父级树中删除 ,并停止任何关联的计时器。在父级被取消或计时器触发之前, 未能调用泄漏相关资源。WithTimeoutContextCancelFunc
CancelFuncContextContext
CancelFuncContext

于 2016-06-17T09:48:09.810 回答
0

Context 包的两个主要用例是:

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

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

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

于 2019-12-23T18:59:38.617 回答