go net/http Client 使用——长连接客户端的使用

go net/http Client使用——长连接客户端的使用_菜的抠脚的博客-CSDN博客 - 图1

hardecz 2017-12-06 15:46:40 go net/http Client使用——长连接客户端的使用_菜的抠脚的博客-CSDN博客 - 图2
17705 go net/http Client使用——长连接客户端的使用_菜的抠脚的博客-CSDN博客 - 图3
go net/http Client使用——长连接客户端的使用_菜的抠脚的博客-CSDN博客 - 图4
收藏 3

最后发布: 2017-12-06 15:46:40 首次发布: 2017-12-06 15:46:40

go net/http Client 使用总结

Client 数据结构

  1. `2. // A Client is an HTTP client. Its zero value (DefaultClient) is a
  2. 3. // usable client that uses DefaultTransport.
  3. 4. //
  4. 5. // The Client's Transport typically has internal state (cached TCP
  5. 6. // connections), so Clients should be reused instead of created as
  6. 7. // needed. Clients are safe for concurrent use by multiple goroutines.
  7. 8. //
  8. 9. // A Client is higher-level than a RoundTripper (such as Transport)
  9. 10. // and additionally handles HTTP details such as cookies and
  10. 11. // redirects.
  11. 12. type Client struct {
  12. 13. // Transport specifies the mechanism by which individual
  13. 14. // HTTP requests are made.
  14. 15. // If nil, DefaultTransport is used.
  15. 16. Transport RoundTripper
  16. 18. // CheckRedirect specifies the policy for handling redirects.
  17. 19. // If CheckRedirect is not nil, the client calls it before
  18. 20. // following an HTTP redirect. The arguments req and via are
  19. 21. // the upcoming request and the requests made already, oldest
  20. 22. // first. If CheckRedirect returns an error, the Client's Get
  21. 23. // method returns both the previous Response (with its Body
  22. 24. // closed) and CheckRedirect's error (wrapped in a url.Error)
  23. 25. // instead of issuing the Request req.
  24. 26. // As a special case, if CheckRedirect returns ErrUseLastResponse,
  25. 27. // then the most recent response is returned with its body
  26. 28. // unclosed, along with a nil error.
  27. 29. //
  28. 30. // If CheckRedirect is nil, the Client uses its default policy,
  29. 31. // which is to stop after 10 consecutive requests.
  30. 32. CheckRedirect func(req *<span class="" "="">Request, via []*Request) error
  31. 34. // Jar specifies the cookie jar.
  32. 35. // If Jar is nil, cookies are not sent in requests and ignored
  33. 36. // in responses.
  34. 37. Jar CookieJar
  35. 39. // Timeout specifies a time limit for requests made by this
  36. 40. // Client. The timeout includes connection time, any
  37. 41. // redirects, and reading the response body. The timer remains
  38. 42. // running after Get, Head, Post, or Do return and will
  39. 43. // interrupt reading of the Response.Body.
  40. 44. //
  41. 45. // A Timeout of zero means no timeout.
  42. 46. //
  43. 47. // The Client cancels requests to the underlying Transport
  44. 48. // using the Request.Cancel mechanism. Requests passed
  45. 50. ay still set Request.Cancel; both will
  46. 51. // cancel the request.
  47. 52. //
  48. 53. // For compatibility, the Client will also use the deprecated
  49. 54. // CancelRequest method on Transport if found. New
  50. 55. // RoundTripper implementations should use Request.Cancel
  51. 56. // instead of implementing CancelRequest.
  52. 57. Timeout time.Duration
  53. 58. }`

注意Transport数据结构,对应如下:

  1. `1. // Transport is an implementation of RoundTripper that supports HTTP,
  2. 2. // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).
  3. 3. //
  4. 4. // By default, Transport caches connections for future re-use.
  5. 5. // This may leave many open connections when accessing many hosts.
  6. 6. // This behavior can be managed using Transport's CloseIdleConnections method
  7. 7. // and the MaxIdleConnsPerHost and DisableKeepAlives fields.
  8. 8. //
  9. 9. // Transports should be reused instead of created as needed.
  10. 10. // Transports are safe for concurrent use by multiple goroutines.
  11. 11. //
  12. 12. // A Transport is a low-level primitive for making HTTP and HTTPS requests.
  13. 13. // For high-level functionality, such as cookies and redirects, see Client.
  14. 14. //
  15. 15. // Transport uses HTTP/1.1 for HTTP URLs and either HTTP/1.1 or HTTP/2
  16. 16. // for HTTPS URLs, depending on whether the server supports HTTP/2.
  17. 17. // See the package docs for more about HTTP/2.
  18. 18. type Transport struct {
  19. 19. idleMu sync.Mutex
  20. 20. wantIdle bool // user has requested to close all idle conns
  21. 21. idleConn map[connectMethodKey][]*persistConn // most recently used at end
  22. 22. idleConnCh map[connectMethodKey]chan *persistConn
  23. 23. idleLRU connLRU
  24. 25. reqMu sync.Mutex
  25. 26. reqCanceler map[*Request]func()
  26. 28. altMu sync.RWMutex
  27. 29. altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
  28. 31. // Proxy specifies a function to return a proxy for a given
  29. 32. // Request. If the function returns a non-nil error, the
  30. 33. // request is aborted with the provided error.
  31. 34. // If Proxy is nil or returns a nil *URL, no proxy is used.
  32. 35. Proxy func(*Request) (*url.URL, error)
  33. 37. // DialContext specifies the dial function for creating unencrypted TCP connections.
  34. 38. // If DialContext is nil (and the deprecated Dial below is also nil),
  35. 39. // then the transport dials using package net.
  36. 40. DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
  37. 42. // Dial specifies the dial function for creating unencrypted TCP connections.
  38. 43. //
  39. 44. // Deprecated: Use DialContext instead, which allows the transport
  40. 45. // to cancel dials as soon as they are no longer needed.
  41. 46. // If both are set, DialContext takes priority.
  42. 47. Dial func(network, addr string) (net.Conn, error)
  43. 49. // DialTLS specifies an optional dial function for creating
  44. 50. // TLS connections for non-proxied HTTPS requests.
  45. 51. //
  46. 52. // If DialTLS is nil, Dial and TLSClientConfig are used.
  47. 53. //
  48. 54. // If DialTLS is set, the Dial hook is not used for HTTPS
  49. 55. // requests and the TLSClientConfig and TLSHandshakeTimeout
  50. 56. // are ignored. The returned net.Conn is assumed to already be
  51. 57. // past the TLS handshake.
  52. 58. DialTLS func(network, addr string) (net.Conn, error)
  53. 60. // TLSClientConfig specifies the TLS configuration to use with
  54. 61. // tls.Client. If nil, the default configuration is used.
  55. 62. TLSClientConfig *tls.Config
  56. 64. // TLSHandshakeTimeout specifies the maximum amount of time waiting to
  57. 65. // wait for a TLS handshake. Zero means no timeout.
  58. 66. TLSHandshakeTimeout time.Duration
  59. 68. // DisableKeepAlives, if true, prevents re-use of TCP connections
  60. 69. // between different HTTP requests.
  61. 70. DisableKeepAlives bool
  62. 72. // DisableCompression, if true, prevents the Transport from
  63. 73. // requesting compression with an "Accept-Encoding: gzip"
  64. 74. // request header when the Request contains no existing
  65. 75. // Accept-Encoding value. If the Transport requests gzip on
  66. 76. // its own and gets a gzipped response, it's transparently
  67. 77. // decoded in the Response.Body. However, if the user
  68. 78. // explicitly requested gzip it is not automatically
  69. 79. // uncompressed.
  70. 80. DisableCompression bool
  71. 82. // MaxIdleConns controls the maximum number of idle (keep-alive)
  72. 83. // connections across all hosts. Zero means no limit.
  73. 84. MaxIdleConns int
  74. 86. // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
  75. 87. // (keep-alive) connections to keep per-host. If zero,
  76. 88. // DefaultMaxIdleConnsPerHost is used.
  77. 89. MaxIdleConnsPerHost int
  78. 91. // IdleConnTimeout is the maximum amount of time an idle
  79. 92. // (keep-alive) connection will remain idle before closing
  80. 93. // itself.
  81. 94. // Zero means no limit.
  82. 95. IdleConnTimeout time.Duration
  83. 97. // ResponseHeaderTimeout, if non-zero, specifies the amount of
  84. 98. // time to wait for a server's response headers after fully
  85. 99. // writing the request (including its body, if any). This
  86. 100. // time does not include the time to read the response body.
  87. 101. ResponseHeaderTimeout time.Duration
  88. 103. // ExpectContinueTimeout, if non-zero, specifies the amount of
  89. 104. // time to wait for a server's first response headers after fully
  90. 105. // writing the request headers if the request has an
  91. 106. // "Expect: 100-continue" header. Zero means no timeout.
  92. 107. // This time does not include the time to send the request header.
  93. 108. ExpectContinueTimeout time.Duration
  94. 110. // TLSNextProto specifies how the Transport switches to an
  95. 111. // alternate protocol (such as HTTP/2) after a TLS NPN/ALPN
  96. 112. // protocol negotiation. If Transport dials an TLS connection
  97. 113. // with a non-empty protocol name and TLSNextProto contains a
  98. 114. // map entry for that key (such as "h2"), then the func is
  99. 115. // called with the request's authority (such as "example.com"
  100. 116. // or "example.com:1234") and the TLS connection. The function
  101. 117. // must return a RoundTripper that then handles the request.
  102. 118. // If TLSNextProto is nil, HTTP/2 support is enabled automatically.
  103. 119. TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper
  104. 121. // MaxResponseHeaderBytes specifies a limit on how many
  105. 122. // response bytes are allowed in the server's response
  106. 123. // header.
  107. 124. //
  108. 125. // Zero means to use a default limit.
  109. 126. MaxResponseHeaderBytes int64
  110. 128. // nextProtoOnce guards initialization of TLSNextProto and
  111. 129. // h2transport (via onceSetNextProtoDefaults)
  112. 130. nextProtoOnce sync.Once
  113. 131. h2transport *http2Transport // non-nil if http2 wired up
  114. 133. // TODO: tunable on max per-host TCP dials in flight (Issue 13957)
  115. 134. }`

实际上是一个连接池,所以在使用的过程中通常是初始化一个TransportClient,在每个处理函数中都共用该连接池,例如如下 Demo:

  1. `
  2. 1. package main
  3. 3. import (
  4. 4. "bytes"
  5. 5. "io/ioutil"
  6. 6. "log"
  7. 7. "net"
  8. 8. "net/http"
  9. 9. "time"
  10. 10. )
  11. 12. var (
  12. 13. httpClient *http.Client
  13. 14. )
  14. 16. // init HTTPClient
  15. 17. func init() {
  16. 18. httpClient = createHTTPClient()
  17. 19. }
  18. 21. const (
  19. 22. MaxIdleConns int = 100
  20. 23. MaxIdleConnsPerHost int = 100
  21. 24. IdleConnTimeout int = 90
  22. 25. )
  23. 27. // createHTTPClient for connection re-use
  24. 28. func createHTTPClient() *http.Client {
  25. 29. client := &http.Client{
  26. 30. Transport: &http.Transport{
  27. 31. Proxy: http.ProxyFromEnvironment,
  28. 32. DialContext: (&net.Dialer{
  29. 33. Timeout: 30 * time.Second,
  30. 34. KeepAlive: 30 * time.Second,
  31. 35. }).DialContext,
  32. 36. MaxIdleConns: MaxIdleConns,
  33. 37. MaxIdleConnsPerHost: MaxIdleConnsPerHost,
  34. 38. IdleConnTimeout: time.Duration(_IdleConnTimeout_)* time.Second,
  35. 39. },
  36. `
  1. Timeout: 20 \* time._Second_,
  1. `1. }
  2. 2. return client
  3. 3. }
  4. 5. func main() {
  5. 6. var endPoint string = "https://localhost:8080/doSomething"
  6. 8. req, err := http.NewRequest("POST", endPoint, bytes.NewBuffer([]byte("Post this data")))
  7. 9. if err != nil {
  8. 10. log.Fatalf("Error Occured. %+v", err)
  9. 11. }
  10. 12. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  11. 14. // use httpClient to send request
  12. 15. response, err := httpClient.Do(req)
  13. 16. if err != nil && response == nil {
  14. 17. log.Fatalf("Error sending request to API endpoint. %+v", err)
  15. 18. } else {
  16. 19. // Close the connection to reuse it
  17. 20. defer response.Body.Close()
  18. 22. // Let's check if the work actually is done
  19. 23. // We have seen inconsistencies even when we get 200 OK response
  20. 24. body, err := ioutil.ReadAll(response.Body)
  21. 25. if err != nil {
  22. 26. log.Fatalf("Couldn't parse response body. %+v", err)
  23. 27. }
  24. 29. log.Println("Response Body:", string(body))
  25. 30. }
  26. 32. }`

下面详细讲解Transport结构需要注意的几个参数设置:

  • DisableKeepAlives
  1. `
  2. 1. // DisableKeepAlives, if true, prevents re-use of TCP connections
  3. 2. // between different HTTP requests.
  4. `

表示是否开启 http keepalive 功能,也即是否重用连接,默认开启 (false)

  • MaxIdleConns
  1. `
  2. 1. // MaxIdleConns controls the maximum number of idle (keep-alive)
  3. 2. // connections across all hosts. Zero means no limit.
  4. `

表示连接池对所有 host 的最大链接数量,host 也即 dest-ip,默认为无穷大(0),但是通常情况下为了性能考虑都要严格限制该数目(实际使用中通常利用压测 二分得到该参数的最佳近似值)。太大容易导致客户端和服务端的 socket 数量剧增,导致内存吃满,文件描述符不足等问题;太小则限制了连接池的 socket 数量,资源利用率较低。

  • MaxIdleConnsPerHost
  1. `
  2. 1. // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
  3. 2. // (keep-alive) connections to keep per-host. If zero,
  4. 3. // DefaultMaxIdleConnsPerHost is used.
  5. `

表示连接池对每个 host 的最大链接数量,从字面意思也可以看出:

  1. `MaxIdleConnsPerHost <= MaxIdleConns`

如果客户端只需要访问一个 host,那么最好将MaxIdleConnsPerHostMaxIdleConns设置为相同,这样逻辑更加清晰。

  • IdleConnTimeout
  1. `
  2. 1. // IdleConnTimeout is the maximum amount of time an idle
  3. 2. // (keep-alive) connection will remain idle before closing
  4. 3. // itself.
  5. 4. // Zero means no limit.
  6. `

空闲 timeout 设置,也即 socket 在该时间内没有交互则自动关闭连接(注意:该 timeout 起点是从每次空闲开始计时,若有交互则重置为 0), 该参数通常设置为分钟级别,例如:90 秒。

  • DialContext
  1. `
  2. 1. // DialContext specifies the dial function for creating unencrypted TCP connections.
  3. 2. // If DialContext is nil (and the deprecated Dial below is also nil),
  4. 3. // then the transport dials using package net.
  5. 4. DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
  6. `

该函数用于创建 http(非 https)连接,通常需要关注TimeoutKeepAlive参数。前者表示建立 Tcp 链接超时时间;后者表示底层为了维持 http keepalive 状态 每隔多长时间发送 Keep-Alive 报文。Timeout通常设置为 30s(网络环境良好),KeepAlive通常设置为 30s(与 IdleConnTimeout 要对应)。

补充说明

Transport数据结构中的MaxConnsPerHost参数目前正在研发中(Go 项目),其表示对每个 host 可以发出的最大连接个数(包括长链接和短链接),是非常有用的参数,可以用来对 socket 数量进行限制,配合MaxIdleConnsMaxIdleConnsPerHost一起使用。

net/http Transport MaxConnsPerHost

参考