我在这里使用半代码只是为了表明我对代码中发生的事情的意图,而不是使问题中的事情复杂化。
我有一个 main.go
文件,它调用连接到 mongoDB 数据库的方法:
mStore := store.NewMongoStore()
在 NewMongoStore
中,我有 client.Connect
用于连接数据库的上下文:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
现在在 main.go
中,我通过这种方式将存储传递到我的路由器 Controller 文件:
routes.GenericRoute(router, mStore)
在 GenericRoute
中,我获取 mStore 并将其传递给函数处理程序:
func GenericRoute(router *gin.Engine, mStore store.Store) {
router.POST("/users", controllers.CreateUser(mStore))
}
现在在 CreateUser
中,我再次创建一个上下文,如下所示,将文档插入到 MongoDB 中:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
insertedId, err := repo.CreateUser(ctx, newUser{"John", "Doe"})
在这里,我将上下文传递给 createUser
以插入新文档。
这种编码的最佳实践是什么?从性能角度来看,哪一个更好?
最佳答案
根据我的经验,Context
有两个主要用例:
- 传递信息。对于您的问题,您可能希望为每个请求生成一个
request_id
并将其传递到代码的最低部分,并记录此request_id
以跨整个请求进行错误跟踪整个代码库。- 此功能并不总是有用,例如您想要初始化 MongoDB 连接,但它是在服务启动期间完成的。此时没有有意义的上下文,带有超时的
context.Background
应该足够了。 - 谨慎对待从
Context
检索的值的变化,如果您到处传递Context
,这可能会导致并发访问。
- 此功能并不总是有用,例如您想要初始化 MongoDB 连接,但它是在服务启动期间完成的。此时没有有意义的上下文,带有超时的
- 自动取消和超时。这两个功能并非凭空而来,您需要调整代码以处理
Context
中的这些信息。但大多数带有 Context 参数的第三方库和标准库都可以很好地处理这两个功能(例如数据库库、HTTP 调用库)。使用此功能,您可以在Context
失效后自动回收资源。- 有时你会想要停止这种级联行为,例如在后台 goroutine 中写入日志,那么你需要创建一个新的
context.Background()
以避免这些写入在上游被取消上下文已取消。context.Background()
还会清除信息上下文,因此有时您需要从上游上下文中提取上下文信息,并手动将它们附加到这个新上下文中。
- 有时你会想要停止这种级联行为,例如在后台 goroutine 中写入日志,那么你需要创建一个新的
对所有函数强制使用 Context
参数有点过分了(没有必要将 Context
添加到简单的 greatestCommonDivisor
函数)但是在您需要的任何地方添加 Context
参数永远不会有什么坏处。 Context
具有足够好的性能,对于您的用例(HTTP 服务器和数据库写入),它不应该对您的服务造成可见的开销。
关于mongodb - 我应该将 context.Context 传递给 Go 中的底层数据库方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73010023/