Golang cli应用程序-如何正确使用上下文?

8yoxcaq7  于 2023-02-06  发布在  Go
关注(0)|答案(2)|浏览(115)

我是golang的新手,对上下文和如何在golang应用程序中使用上下文有些困惑,特别是我正在使用cli应用程序,只需要访问mongo。
就像-我只是创建了一个共享ctx上下文变量,然后将其用于需要上下文的任何操作,这对吗?
任何需要上下文的操作会重新启动5秒计时器吗?或者这是一个共享计时器?

package main

import (
    "context"
    "log"
    "os"
    "time"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/mongo/readpref"
)

func main() {

    log.SetOutput(os.Stdout)

    // Create a context with a timeout of 5 seconds
    //This defines a timeout context that will be canceled after 5 seconds.
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    // always defer in case func returns early
    defer cancel()

    //Creates a new ClientOptions instance.
    clientOptions := options.Client()
    clientOptions = clientOptions.ApplyURI("mongodb+srv://127.0.0.1?retryWrites=true&w=majority")

    //Connect to mongo
    client, err := mongo.Connect(ctx, clientOptions)
    defer client.Disconnect(ctx)
    if err != nil {
        log.Fatal(err)
    }

    //Test connection to the database
    log.Println("I: test mongo connection using ping")
    err = client.Ping(ctx, readpref.Primary())
    if err != nil {
        log.Fatal(err)
    }

    log.Println("I: Fin")
}
uqdfh47h

uqdfh47h1#

如果您仔细考虑一下,就会发现“水平”共享context.Context是没有意义的(意味着不属于同一调用堆栈的操作之间)。golang Context提供了操作(包括调用栈中在其下的任何嵌套操作)要被执行-诸如“在X秒内”,以防止由于通信延迟而挂起,所以如果你并行发出10个请求,你应该给每个请求一个自己的上下文-你可能不希望第十个请求失败,因为第一个失败了。如果你只是使用context.Background()context.TODO(),没有进一步的修饰,你可能不需要在你第一次创建Context的时候把它存储在一个变量中--你可以在把它传递给调用栈中的第一个函数的时候创建它,并且适当构造的代码将适当地将其向下传递到堆栈,在此过程中应用必要的修饰:

func Execute() {
  DoThing(context.Background())
  // Other stuff
}

func DoThing(pctx context.Context) {
  ctx, cancel := context.WithTimeout(pctx, 10 * time.Second) // Timeout after 10 seconds
  defer cancel()

  DoThingThatMayTakeAWhile(ctx)
  select {
    // You may want other stuff here
    case <-ctx.Done():
      // Context timed out, maybe log an error?
  }
}

func DoThingThatMayTakeAWhile(pctx context.Context) {
  DoThingNeedingInfoInContext(context.WithValue(pctx, "thisisakey", "value"))
}

func DoThingNeedingInfoInContext(ctx context.Context) {
  val := ctx.Value("thisisakey")
  // Do something with val, check that it isn't nil, etc.
}

如果我要对DoThingThatMayTakeAWhile()进行多次调用,我希望给每个调用一个单独的子上下文--我不希望与每个调用共享ctx
因此,在 * 您的 * 代码中,对mongo.Connect()的每个调用都应该接收一个新创建的context.Context示例。

yqyhoc1h

yqyhoc1h2#

为每个需要上下文的操作创建一个新的上下文变量,上下文的计时器将永远不会重新启动。
在您的示例中,尝试在context.WithTimeout之后添加time.Sleep(6*time.Second),您将看到所有操作都返回错误context deadline exceeded

相关问题