为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕姐8265434 2022-06-27 17:19:49
我目前正在开发一个设计为在 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 是不是一个坏主意?
查看完整描述

2 回答

?
海绵宝宝撒

TA贡献1809条经验 获得超8个赞

好的,经过更多思考后,我更改了源代码,如果您认为这是解决我的问题的好方法,请告诉我。我仍在学习 Go 和 HTTP 服务器,因此感谢任何输入。


这是我的修复/想法:


以前创建容器后,我只是返回了它的 IP 地址并忘记了它。


现在我创建了一个重复尝试向服务器发送 POST 请求的 go 例程。如果它没有失败,那么我通过通道发送 true 并关闭函数。


    IP := info.NetworkSettings.Networks[networkName].IPAddress

    works := make(chan bool)

    ctx, canelRoutine := context.WithCancel(context.Background())

    defer canelRoutine()


    go func(ctx context.Context) {

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

            "Code": "print()",

        })

        select {

        case <-ctx.Done():

            return

        default:

            for {

                _, err := http.Post(

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

                    "application/json",

                    bytes.NewBuffer(requestBody),

                )

                if err == nil {

                    works <- true

                    return

                }

            }

        }

    }(ctx)


发送 goroutine 后,我创建了一个计时器并等待计时器返回或 goroutine。


    timer := time.After(500 * time.Millisecond)

    select {

    case <-works:

        return IP, nil

    case <-timer:

        return IP, &UnreachableContainerError{name: schedulerName}

    }

这个解决方案的好处是我现在引入了一个 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) {

    ///Defaults

    dockerfile := "Dockerfile_standard"

    networkName := "scheduler-cluster"

    imageID := "lkelly93/scheduler_image:latest"


    cli, err := client.NewEnvClient()

    if err != nil {

        return "", err

    }


    err = createDefaultImageIfNeeded(

        cli,

        imageID,

        dockerfile)


    if err != nil {

        return "", err

    }


    err = createSchedulerClusterNetworkIfNeeded(cli, networkName)

    if err != nil {

        return "", err

    }


    ctx := context.Background()

    resp, err := cli.ContainerCreate(

        ctx,

        &container.Config{Image: imageID},

        &container.HostConfig{

            NetworkMode: container.NetworkMode(networkName),

            Privileged:  true,

        },

        nil,

        schedulerName,

    )


    if err != nil {

        return "", err

    }


    err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})

    if err != nil {

        return "", err

    }


    //Get container IP

    info, err := cli.ContainerInspect(ctx, resp.ID)

    if err != nil {

        return "", err

    }


    IP := info.NetworkSettings.Networks[networkName].IPAddress

    works := make(chan bool)

    ctx, canelRoutine := context.WithCancel(context.Background())

    defer canelRoutine()


    go func(ctx context.Context) {

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

            "Code": "print()",

        })

        select {

        case <-ctx.Done():

            return

        default:

            for {

                _, err := http.Post(

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

                    "application/json",

                    bytes.NewBuffer(requestBody),

                )

                if err == nil {

                    works <- true

                    return

                }

            }

        }

    }(ctx)


    timer := time.After(500 * time.Millisecond)

    select {

    case <-works:

        return IP, nil

    case <-timer:

        return IP, &UnreachableContainerError{name: schedulerName}

    }

}


查看完整回答
反对 回复 2022-06-27
?
哔哔one

TA贡献1854条经验 获得超8个赞

您可以在容器内运行测试代码,通过 docker compose 使用选项depends_on 启动



查看完整回答
反对 回复 2022-06-27
  • 2 回答
  • 0 关注
  • 135 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号