Gin - Gonic 上下文在子 go 例程中被取消

问题描述 投票:0回答:1

我正在使用 Gin Web 框架作为 Go API 后端服务。在 API 端点的 gin 处理程序函数内,我启动一个 Go 例程来运行一些后台任务。这些任务的运行应该不依赖于发送到客户端的响应。但是,在子 Go 例程中进行数据库操作时,我经常遇到“上下文已取消”错误,并且后台操作失败。

我无法创建一个新的上下文并将其作为 gin 上下文传递一些我需要的数据,并且有一些我需要重用的现有函数,这些函数需要

*gin.context
作为参数。

我做了一些调试,发现错误是由于子例程使用与父例程相同的

*gin.context
(即处理程序本身)而发生的。将响应发送到客户端后,上下文被取消,这会导致数据库操作出错。

我读到一个解决方案,说使用

context.Background()
创建一个新的上下文并将其传递。但我需要使用
*gin.context
本身,否则我将不得不重写很多函数并复制一些数据。

我看到的另一个有希望的解决方案是建议使用

*gin.context.Copy()
。在
Copy()
的杜松子酒文档中,明确指出:

Copy 返回当前上下文的副本,可以在请求范围之外安全地使用。
当上下文必须传递给 goroutine 时必须使用它。

我期待

Copy()
能够正常工作。但即使我传递了复制的上下文,我也会得到相同的上下文取消错误。

以下是我所做的:

func SendApiResponseToClient(c *gin.Context) {
  response := doSomeOperations(c)
  
  newContext := c.Copy()
  go doSomeBackGroundOperations(newContext, response)

  sendResponse(response)
}

doSomeBackGroundOperations()
函数有一些数据库操作,这就是失败的原因。

为了确认除上下文之外的所有其他内容都正常,我在将 API 响应发送到客户端之前添加了一秒超时,以等待后台操作完成。在这种情况下,我没有遇到任何错误,一切都按预期进行。

我还在 gin-gonic/gin 存储库中发现了一些 github 未解决的问题,如下所示:

我相信这些问题在某种程度上与我面临的问题有关。但是,我仍然找不到明确的解决方案。

任何可能对我有帮助的建议将不胜感激。 谢谢

go go-gin go-context
1个回答
0
投票

*gin.Context
包含一个标准库
context.Context
埋在
*http.Request
中 - 这实际上是负责管理取消的。

以下代码片段可以复制 Gin 上下文,并附加背景上下文:

func detachContext(c *gin.Context) *gin.Context {
  // First copy the gin.Context, this doesn't actually copy the
  // underlying http.Request, but references it.
  copy := c.Copy()
  // As we don't want to modify the existing http.Request,
  // we want to use Clone, which handily allows us to specify
  // a new context.Context.
  copy.Request = copy.Request.Clone(context.Background())
  return copy
}

请记住:

  • 新创建的上下文没有取消,这可能会导致 Go 例程的累积。您可能需要使用
    context.WithTimeout
    或类似的工具来控制它。
  • 已存储在
    context.Context
    中的任何值都不会包含在新的
    context.Context
    中。这应该没问题,因为 Gin 使用它自己的 Context 类型来存储重要值。

最后,说了这么多,我建议避免

*gin.Context
太深入地渗透你的代码。这是杜松子酒特有的。相反,我建议您在 HTTP 处理程序层从中提取所需的信息,然后直接将此信息传递给其他代码单元。这将减少整个项目与 Gin 的配合量。

© www.soinside.com 2019 - 2024. All rights reserved.