我目前正在努力寻找在Golang中发布HTTP帖子时重用连接的方法.
我创建了一个像这样的传输和客户端:
// Create a new transport and HTTP client
tr := &http.Transport{}
client := &http.Client{Transport: tr}
Run Code Online (Sandbox Code Playgroud)
然后我将这个客户端指针传递给一个goroutine,它会向同一个端点发送多个帖子,如下所示:
r, err := client.Post(url, "application/json", post)
Run Code Online (Sandbox Code Playgroud)
查看netstat,这似乎导致每个帖子的新连接导致大量并发连接被打开.
在这种情况下重用连接的正确方法是什么?
Mat*_*elf 83
在调用之前,您应该确保阅读完毕,直到响应完成Close().
例如
res, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
Run Code Online (Sandbox Code Playgroud)
要确保http.Client连接重用,请务必执行以下两项操作:
ioutil.ReadAll(resp.Body))Body.Close()DrJ*_*000 36
编辑:对于为每个请求构建传输和客户端的人来说,这更像是一个注释.
Edit2:更改了godoc的链接.
Transport是用于重用连接的结构; 请参阅https://godoc.org/net/http#Transport ("默认情况下,传输缓存连接以供将来重复使用.")
因此,如果您为每个请求创建一个新的传输,它将每次创建新的连接.在这种情况下,解决方案是在客户端之间共享一个Transport实例.
bn0*_*00d 36
如果有人仍在寻找如何做到这一点的答案,那么我就是这样做的.
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"time"
)
var httpClient *http.Client
const (
MaxIdleConnections int = 20
RequestTimeout int = 5
)
func init() {
httpClient = createHTTPClient()
}
// createHTTPClient for connection re-use
func createHTTPClient() *http.Client {
client := &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: MaxIdleConnections,
},
Timeout: time.Duration(RequestTimeout) * time.Second,
}
return client
}
func main() {
endPoint := "https://localhost:8080/doSomething"
req, err := http.NewRequest("POST", endPoint, bytes.NewBuffer([]byte("Post this data")))
if err != nil {
log.Fatalf("Error Occured. %+v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
response, err := httpClient.Do(req)
if err != nil && response == nil {
log.Fatalf("Error sending request to API endpoint. %+v", err)
}
// Close the connection to reuse it
defer response.Body.Close()
// Let's check if the work actually is done
// We have seen inconsistencies even when we get 200 OK response
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatalf("Couldn't parse response body. %+v", err)
}
log.Println("Response Body:", string(body))
}
Run Code Online (Sandbox Code Playgroud)
去游乐场:http://play.golang.org/p/oliqHLmzSX
总之,我正在创建一个不同的方法来创建HTTP客户端并将其分配给全局变量,然后使用它来发出请求.请注意
defer response.Body.Close()
Run Code Online (Sandbox Code Playgroud)
这将关闭连接并将其设置为可以再次使用.
希望这会对某人有所帮助.
关于身体
// It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
Run Code Online (Sandbox Code Playgroud)
所以如果要复用TCP连接,每次读完就得关闭Body。此外,使用defer,您可以确保Body.Close()毕竟被调用。建议使用函数 ReadBody(io.ReadCloser) 像这样。
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://github.com", nil)
if err != nil {
fmt.Println(err.Error())
return
}
client := &http.Client{}
i := 0
for {
resp, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
_, _ = readBody(resp.Body)
fmt.Println("done ", i)
time.Sleep(5 * time.Second)
}
}
func readBody(readCloser io.ReadCloser) ([]byte, error) {
defer readCloser.Close()
body, err := ioutil.ReadAll(readCloser)
if err != nil {
return nil, err
}
return body, nil
}
Run Code Online (Sandbox Code Playgroud)
并且不要像下面这样调用 Close:
res, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body) // what if io.Copy panics, res.Body.Close() will not called.
res.Body.Close()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
58205 次 |
| 最近记录: |