我有一个使用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
答案 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)
类型的通道,对其进行两次循环,并检查遇到的第一个错误。