正确使用go context.Context

时间:2015-06-19 00:20:01

标签: concurrency go gorilla

我刚刚阅读了文章:Build You Own Web Framework In Go并且为了在处理程序之间共享值,我选择了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}
    //....
}

一切正常,处理程序加载速度比使用大猩猩/上下文更快所以我的问题是:

  1. 这种方法安全吗?
  2. 是否真的有必要按照我的方式推迟c.cancel()函数?
  3. 我可以使用它来实现自定义Web框架,使用struct之类的控制器来与模型共享值吗?

3 个答案:

答案 0 :(得分:1)

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

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

此外,您不需要具有取消的上下文。 context.Context可以用于根上下文。

答案 1 :(得分:1)

注意:去1.7.0-rc2确实澄清了一点how to release resources associated with ContextsSameer Ajmani):

  

有些用户没有意识到创建ContextCancelFunc将子树附加到父级,并且该子树在CancelFunc之前不会被释放被叫或父母被取消

     

在软件包文档的早期明确说明,以便了解此软件包的人员具有正确的概念模型。

documentation now includes

  

对服务器的传入请求应创建Context,对服务器的传出呼叫应接受Context
  它们之间的函数链调用必须传播Context,可选地将其替换为使用ContextWithCancelWithDeadline或{{WithTimeout创建的派生WithValue。 1}}。
  这些Context值组成了一个树:取消Context时,从中导出的所有Contexts也会被取消

     

WithCancelWithDeadlineWithTimeout函数会返回派生的ContextCancelFunc
  调用CancelFunc取消新的Context以及从中派生的任何上下文,从父树中删除Context,并停止所有关联的计时器。
  未能调用CancelFunc会泄漏相关资源,直到取消父Context或计时器触发。

答案 2 :(得分:0)

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/