首页 > 解决方案 > 重用 mongodb 连接时处理上下文

问题描述

我通过client作为参数传递使多个 goroutine 共享一个连接。

uri := "mongodb://localhost:27017"
ctx := context.Background()
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))

go Foo(client)
go Bar(client)

func Foo(client *mongo.Client) {
        // ... 
}

func Bar(client *mongoClient) {
        // ...
}

我对如何处理感到困惑ctx。我应该在每次查询数据库时创建一个新的上下文,还是应该像客户端一样重用上下文?

标签: mongodbgo

解决方案


这取决于您FooBar方法的行为方式。让我们假设该Foo方法是一个简单的短期 goroutine,它对 DB 进行一次查询,而您唯一需要做的就是检查其父上下文是否未完成或已取消。然后你可以为你的 Foo 方法提供父上下文。

func main() {
    uri := "mongodb://localhost:27017"
    ctx := context.Background()
    client, err := Connect(ctx, uri)

    ctx, cancel := context.WithCancel(ctx)

    
    if err != nil {
        panic(err)
    }

    go Foo(ctx, client)
    go Bar(context.WithValue(ctx, "uri", uri), client)

    // cancel parent context
    cancel()

    time.Sleep(5*time.Second)
}

func Foo(ctx context.Context, client *Client) {
    fmt.Printf("Foo: %s\n", ctx.Value("uri"))
    select {
        case <- ctx.Done():
            err := ctx.Err()
            if err != nil {
                // you could switch for the actual reason
                fmt.Println("In our case context canceled: ", err)
                return
            }
            fmt.Printf("Do something...")
    }
}

另一方面,如果Bar执行一些重要的逻辑并对 DB 进行多次调用,您可能希望单独的上下文能够与父上下文分开取消它。然后你可以从你的父母那里得到一个新的背景。

func Bar(ctx context.Context, client *Client) {
    // Bar has a non trivial logic and needs a separate cancellation and handling
    ctx, cancelFunc := context.WithCancel(ctx)
    fmt.Printf("Bar: %s\n", ctx.Value("uri"))
    
    // cancel derived context
    cancelFunc()

}

推荐阅读