猿问

Docker 容器连接中的服务器被拒绝,我应该在测试中添加 time.Sleep

我目前正在开发一个设计为在 Docker 容器中运行的服务器。


这是我的测试设置方法:


func TestMain(m *testing.M) {

    schedulerName := "scheduler1"

    IP, err := container.StartNewScheduler(schedulerName)

    if err != nil {

        log.Println("Could not create container.")

        log.Fatal(err)

    }

    serverIP = IP

    code := m.Run()

    cleanupContainer(schedulerName)

    os.Exit(code)

}

该行container.StartNewScheduler(schedulername)启动了一个名为“scheduler1”的新 docker 容器,并告诉它在其中运行服务器。


接下来我使用在后台运行的容器运行我的测试,现在我只有一个测试。


func TestNewScheduler(t *testing.T) {

    testCodeInput := "THIS IS A TEST"

    requestBody, err := json.Marshal(map[string]string{

        "Code": fmt.Sprintf("print(\"%s\")", testCodeInput),

    })


    if err != nil {

        t.Fatal(err)

    }


    url := fmt.Sprintf("http://%s:%d/execute/python", serverIP, 3000)

    contentType := "application/json"

    body := bytes.NewBuffer(requestBody)


    response := post(url, contentType, body, t)

    actual := parseOutput(response.Body, t)

    response.Body.Close()


    expected := fmt.Sprintf("{\"Stdout\":\"%s\\n\"}", testCodeInput)

    if actual != expected {

        t.Fatalf("Expected %s, but got %s", expected, actual)

    }

}

我遇到的问题是有时我的连接被拒绝,有时我没有。


server_container_test.go:51: Post http://172.20.0.2:3000/execute/python: dial tcp 172.20.0.2:3000: connect: connection refused

我注意到,每当我尝试调试问题时,一切似乎都运行良好。我的运行理论是因为当我单步执行代码时,容器有更多时间启动并让服务器在其中运行。


为了测试我的假设,我在我的 post 方法中添加了第二个 post 调用,并在调用它之前设置了一个计时器。


还有其他人对可能导致我这个问题的原因有任何猜测吗?


如果我的假设是正确的,这是解决此问题的最佳方法吗?在测试中添加 time.Sleep 是不是一个坏主意?


慕姐8265434
浏览 136回答 2
2回答

海绵宝宝撒

好的,经过更多思考后,我更改了源代码,如果您认为这是解决我的问题的好方法,请告诉我。我仍在学习 Go 和 HTTP 服务器,因此感谢任何输入。这是我的修复/想法:以前创建容器后,我只是返回了它的 IP 地址并忘记了它。现在我创建了一个重复尝试向服务器发送 POST 请求的 go 例程。如果它没有失败,那么我通过通道发送 true 并关闭函数。&nbsp; &nbsp; IP := info.NetworkSettings.Networks[networkName].IPAddress&nbsp; &nbsp; works := make(chan bool)&nbsp; &nbsp; ctx, canelRoutine := context.WithCancel(context.Background())&nbsp; &nbsp; defer canelRoutine()&nbsp; &nbsp; go func(ctx context.Context) {&nbsp; &nbsp; &nbsp; &nbsp; requestBody, _ := json.Marshal(map[string]string{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Code": "print()",&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case <-ctx.Done():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _, err := http.Post(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Sprintf("http://%s:%d/execute/python", IP, 3000),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "application/json",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bytes.NewBuffer(requestBody),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; works <- true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }(ctx)发送 goroutine 后,我创建了一个计时器并等待计时器返回或 goroutine。&nbsp; &nbsp; timer := time.After(500 * time.Millisecond)&nbsp; &nbsp; select {&nbsp; &nbsp; case <-works:&nbsp; &nbsp; &nbsp; &nbsp; return IP, nil&nbsp; &nbsp; case <-timer:&nbsp; &nbsp; &nbsp; &nbsp; return IP, &UnreachableContainerError{name: schedulerName}&nbsp; &nbsp; }这个解决方案的好处是我现在引入了一个 UnreachableContainerError ,它允许我更具体地了解我的错误消息,并且可以在接收端进行检查。我还会以任何一种方式发回 IP 地址,以防客户端出于其他原因需要它。如果您想查看它,这是完整的 StartNewScheduler 方法。//StartNewScheduler starts a new scheduler with the given options.//returns the IP address for the given scheduler.func StartNewScheduler(schedulerName string) (string, error) {&nbsp; &nbsp; ///Defaults&nbsp; &nbsp; dockerfile := "Dockerfile_standard"&nbsp; &nbsp; networkName := "scheduler-cluster"&nbsp; &nbsp; imageID := "lkelly93/scheduler_image:latest"&nbsp; &nbsp; cli, err := client.NewEnvClient()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; err = createDefaultImageIfNeeded(&nbsp; &nbsp; &nbsp; &nbsp; cli,&nbsp; &nbsp; &nbsp; &nbsp; imageID,&nbsp; &nbsp; &nbsp; &nbsp; dockerfile)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; err = createSchedulerClusterNetworkIfNeeded(cli, networkName)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; ctx := context.Background()&nbsp; &nbsp; resp, err := cli.ContainerCreate(&nbsp; &nbsp; &nbsp; &nbsp; ctx,&nbsp; &nbsp; &nbsp; &nbsp; &container.Config{Image: imageID},&nbsp; &nbsp; &nbsp; &nbsp; &container.HostConfig{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NetworkMode: container.NetworkMode(networkName),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Privileged:&nbsp; true,&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; nil,&nbsp; &nbsp; &nbsp; &nbsp; schedulerName,&nbsp; &nbsp; )&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; //Get container IP&nbsp; &nbsp; info, err := cli.ContainerInspect(ctx, resp.ID)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return "", err&nbsp; &nbsp; }&nbsp; &nbsp; IP := info.NetworkSettings.Networks[networkName].IPAddress&nbsp; &nbsp; works := make(chan bool)&nbsp; &nbsp; ctx, canelRoutine := context.WithCancel(context.Background())&nbsp; &nbsp; defer canelRoutine()&nbsp; &nbsp; go func(ctx context.Context) {&nbsp; &nbsp; &nbsp; &nbsp; requestBody, _ := json.Marshal(map[string]string{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Code": "print()",&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case <-ctx.Done():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _, err := http.Post(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Sprintf("http://%s:%d/execute/python", IP, 3000),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "application/json",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bytes.NewBuffer(requestBody),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; works <- true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }(ctx)&nbsp; &nbsp; timer := time.After(500 * time.Millisecond)&nbsp; &nbsp; select {&nbsp; &nbsp; case <-works:&nbsp; &nbsp; &nbsp; &nbsp; return IP, nil&nbsp; &nbsp; case <-timer:&nbsp; &nbsp; &nbsp; &nbsp; return IP, &UnreachableContainerError{name: schedulerName}&nbsp; &nbsp; }}

哔哔one

您可以在容器内运行测试代码,通过 docker compose 使用选项depends_on 启动。
随时随地看视频慕课网APP

相关分类

Go
我要回答