我应该在每个传入请求中创建新的上下文吗?

Xor*_*oso 2 http request go

在过去的几天里,我一直在阅读有关 Go 的内容,并且我不断回顾的一个概念是上下文。

我想我理解创建这样一个结构背后的动机。我不明白的是在传入 HTTP 请求中使用上下文时的特定用例。

假设我们有一个追随者httpHandlerFunc。在该处理程序内部,我们调用一个需要传递上下文的函数。我经常看到这个解决方案

func myHandler(w http.ResponseWriter, r *http.Request) {
  ctx := context.WithValue(context.Background(), "request", r)
  otherFunc(ctx)
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么我们不直接从请求中传递上下文,就像这样

func myHandler(w http.ResponseWriter, r *http.Request) {
  otherFunc(r.Context())
}
Run Code Online (Sandbox Code Playgroud)

既然我们希望上下文流过我们的程序,那么传递请求的上下文不是更有意义吗?我认为创建后台上下文是我们只想在根父级中执行的操作,就像init()函数一样。

kos*_*tix 7

你可能错过了主要内容由于您正在处理的 HOWTO 较差,

\n

在上下文中携带任意值的可能性实际上是这种类型的一个缺陷,其设计者对此感到遗憾,因为它创建了一种反模式(处理上下文作为某种状态的正确方法是明确地拥有一组值)传遍了)。

\n

上下文存在的主要原因是它们提供了信号的树状传播(在上下文的情况下是取消或“完成”)。\n因此上下文背后的原始想法如下:

\n
    \n
  1. “根”上下文对象是为传入请求创建的。

    \n
  2. \n
  3. 需要代表请求执行的每个“任务”都与其自己的上下文相关联,该上下文源自请求\xc2\xb9。

    \n
  4. \n
  5. 这些任务可能会产生其他任务等等。

    \n

    正如您所看到的,形成了“工作单元”的层次结构,\xe2\x80\x94 链接到对象,这是这些单元存在和执行的原因。

    \n
  6. \n
  7. 当传入请求被取消时(例如,客户端的套接字断开连接),与其关联的上下文对象也会被取消,然后所有链接的任务都会收到它,因为它是从根节点传播的。生成的上下文树一直到其叶子 \xe2\x80\x94确保所有任务都被执行请求

    \n

    当然,为了使其工作,每个“任务” \xe2\x80\x94 通常是一个执行某些操作 \xe2\x80\x94 的 Goroutine 需要从传递给它的上下文中“监听”以完成“完成”信号。

    \n
  8. \n
\n

上下文还支持开箱即用的超时,因此您可以创建一个上下文,该上下文在经过一些固定时间间隔后会自行取消。

\n

那么,回到你问题中的例子。

\n

第一个示例完全忽略请求的上下文,并从头开始创建上下文,表面上唯一的原因是在其中携带内容(不好)。

\n

第二个示例可能将上下文用于其预期目的(但我们不知道,因为我们看不到otherFunc)。

\n

我建议您阅读https://blog.golang.org/context,以及那里链接的有关 Go 并发模式的文章。

\n
\n

\xc2\xb9\xc2\xa0实际上,如果要控制的任务没有其他策略“添加”到现有的父上下文,则不需要创建新的上下文。\n这里派生的思想是为了实现附加的取消此特定任务中的工作以及尊重父上下文取消的方法。\n例如,为特定任务派生的上下文可以有自己的截止日期,或者有办法仅取消此特定上下文。
\n当然,可以为任务派生出复杂的\xe2\x80\x94nested\xe2\x80\x94上下文:例如,可以从父上下文派生出具有截止日期的上下文,然后可以从父上下文派生出可取消的上下文前者。结果将是一个上下文被代码明确地取消,或者当截止日期到期或当父上下文发出取消信号时被取消。

\n

  • @Xoroxoxoxoxoso你没有错过任何东西,这就是你“今天”应该这样做的方式。博客文章发表于 2014 年,而 2016 年发布的 Go 1.7 中才添加了上下文支持 `http.Request`。所以在 2014 年 `Request.Context()` 还不存在。 (3认同)