-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add connection pooling and keep-alive #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package httpclient | ||
|
|
||
| import ( | ||
| "crypto/tls" | ||
| "net" | ||
| "net/http" | ||
| "net/http/httptrace" | ||
| "sync/atomic" | ||
| "time" | ||
| ) | ||
|
|
||
| // Metrics holds the connection reuse metrics. | ||
| type Metrics struct { | ||
| ConnectionsReused int64 | ||
| ConnectionsCreated int64 | ||
| } | ||
|
|
||
| // Options represents the configuration for the HTTP client. | ||
| type Options struct { | ||
| MaxPerHost int | ||
| MaxIdle int | ||
| IdleTimeout time.Duration | ||
| NoKeepAlive bool | ||
| HTTP1 bool | ||
| } | ||
|
|
||
| // New creates a new HTTP client with the given options. | ||
| func New(opts Options) (*http.Client, *Metrics) { | ||
| metrics := &Metrics{} | ||
| transport := &http.Transport{ | ||
| Proxy: http.ProxyFromEnvironment, | ||
| DialContext: (&net.Dialer{ | ||
| Timeout: 30 * time.Second, | ||
| KeepAlive: 30 * time.Second, | ||
| }).DialContext, | ||
| MaxIdleConns: opts.MaxIdle, | ||
| IdleConnTimeout: opts.IdleTimeout, | ||
| TLSHandshakeTimeout: 10 * time.Second, | ||
| MaxConnsPerHost: opts.MaxPerHost, | ||
| DisableKeepAlives: opts.NoKeepAlive, | ||
| } | ||
|
Comment on lines
+30
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of creating a new transport := http.DefaultTransport.(*http.Transport).Clone()
transport.MaxIdleConns = opts.MaxIdle
transport.IdleConnTimeout = opts.IdleTimeout
transport.MaxConnsPerHost = opts.MaxPerHost
transport.DisableKeepAlives = opts.NoKeepAlive |
||
|
|
||
| if opts.HTTP1 { | ||
| // Disable HTTP/2 by preventing the TLS next protocol negotiation. | ||
| transport.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper) | ||
| } | ||
|
|
||
| return &http.Client{ | ||
| Transport: &metricsRoundTripper{ | ||
| transport: transport, | ||
| metrics: metrics, | ||
| }, | ||
| }, metrics | ||
| } | ||
|
|
||
| // metricsRoundTripper is a custom http.RoundTripper that collects connection reuse metrics. | ||
| type metricsRoundTripper struct { | ||
| transport http.RoundTripper | ||
| metrics *Metrics | ||
| } | ||
|
|
||
| func (m *metricsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | ||
| trace := &httptrace.ClientTrace{ | ||
| GotConn: func(info httptrace.GotConnInfo) { | ||
| if info.Reused { | ||
| atomic.AddInt64(&m.metrics.ConnectionsReused, 1) | ||
| } else { | ||
| atomic.AddInt64(&m.metrics.ConnectionsCreated, 1) | ||
| } | ||
| }, | ||
| } | ||
| req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) | ||
| return m.transport.RoundTrip(req) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The errors returned from parsing these new command-line flags are being ignored. If a user provides an invalid value for any of these flags (e.g.,
--max-connections=foo), the error will be discarded, and the variable will be assigned its zero value. This can lead to the application running with an unintended configuration without any warning. The errors should be checked and returned to the user, so they are aware of the issue.