多阅读器的并发POST不返回响应

时间:2018-08-21 20:00:24

标签: go

我有一个使用echo的概念证明http服务器,该服务器接受带有JSON正文的POST请求。我正在尝试使用管道和multiwriter将请求主体流式传输到多个POST请求,但是它无法正常工作。

在下面的示例中,我可以看到数据已发送到2个POST端点,并且可以看到这些请求的日志,但是我再也没有收到响应,似乎代码已挂起,等待http.Post(...)函数执行完成。

如果我直接调用这两个端点,它们可以正常工作并给出有效的json响应,所以我认为问题出在这条代码,这是我的路由处理程序。

func ImportAggregate(c echo.Context) error {
    oneR, oneW := io.Pipe()
    twoR, twoW := io.Pipe()

    done := make(chan bool, 2)

    go func() {
        fmt.Println("Product Starting")
        response, err := http.Post("http://localhost:1323/products/import", "application/json", oneR)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(response.Body)
        }
        done <- true
    }()

    go func() {
        fmt.Println("Import Starting")
        response, err := http.Post("http://localhost:1323/discounts/import", "application/json", twoR)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(response.Body)
        }
        done <- true
    }()

    mw := io.MultiWriter(oneW, twoW)
    io.Copy(mw, c.Request().Body)

    <-done
    <-done

    return c.String(200, "Imported")
}

控制台中的输出为:

Product Starting
Import Starting

1 个答案:

答案 0 :(得分:0)

OP代码中的问题是http.Post调用永远不会检测到所提供的io.Reader的EOF。

发生这种情况是因为提供的一半写入管道从未关闭,因此,一半读取管道从未发出常规EOF错误。

关于OP注释,即关闭半读管道会产生不规则的错误,请注意,从关闭的管道进行读取不是正确的行为。

因此,在这种情况下,应注意在内容复制后立即关闭写操作的一半。

结果源代码应更改为

func ImportAggregate(c echo.Context) error {
    oneR, oneW := io.Pipe()
    twoR, twoW := io.Pipe()

    done := make(chan bool, 2)

    go func() {
        fmt.Println("Product Starting")
        response, err := http.Post("http://localhost:1323/products/import", "application/json", oneR)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(response.Body)
        }
        done <- true
    }()

    go func() {
        fmt.Println("Import Starting")
        response, err := http.Post("http://localhost:1323/discounts/import", "application/json", twoR)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(response.Body)
        }
        done <- true
    }()

    mw := io.MultiWriter(oneW, twoW)
    io.Copy(mw, c.Request().Body)
    oneW.Close()
    twoW.Close()

    <-done
    <-done

    return c.String(200, "Imported")
}

关于OP问题的旁注:

  • 必须在io.Copy周围实施错误检查,以检测传输错误。

  • 不需要关闭管道的读取半边,http.Post会在收到EOF信号后执行此操作。

  • 负责消耗管道的goroutine必须在复制输入请求之前声明并启动。 Pipes是同步的,代码将在io.Copy期间阻塞,等待另一端使用。

  • 完成的chan不需要无缓冲(长度2)

  • 一种将错误从传出请求转发到传出响应的方法是使用(chan error)类型的通道,对其进行两次循环,并检查遇到的第一个错误。