Skip to content

Instantly share code, notes, and snippets.

@moonsub-kim
Last active February 8, 2024 03:20
Show Gist options
  • Save moonsub-kim/6246802d85e16d56609da06af4083065 to your computer and use it in GitHub Desktop.
Save moonsub-kim/6246802d85e16d56609da06af4083065 to your computer and use it in GitHub Desktop.
http connection pool in go explained
var startTime time.Time
trace := &httptrace.ClientTrace {
TLSHandshakeStart: func() {
startTime = time.Now()
},
TLSHandshakeDone: func(s tls.ConnectionState, err error) {
fmt.Sprintf("%v elapsed", time.Now() - startTime)
},
}
traceCtx := httptrace.WithClientTrace(ctx, trace)
req := http.Request{ ... }
req.WithContext(traceCtx)
res, err := client.Do(req)
...
package httptrace
import (
"context"
"crypto/tls"
"log"
"net/http"
"net/http/httptrace"
"time"
)
// TransportWithTrace wraps Transport
type TransportWithTrace struct {
roundtripper http.RoundTripper
}
// RoundTrip wraps round trip and add traces
func (t *TransportWithTrace) RoundTrip(req *http.Request) (res *http.Response, err error) {
ctx := t.withTLSTrace(req.Context())
req = req.WithContext(ctx)
startTime := time.Now()
res, err = t.roundtripper.RoundTrip(req)
log.Printf("RoundTrip:: %v elapsed", time.Now() - startTime)
}
func (t *TransportWithTrace) withTLSTrace(ctx context.Context) context.Context {
var startTime time.Time
clientTrace := &httptrace.ClientTrace{
TLSHandshakeStart: func() {
startTime = time.Now()
},
TLSHandshakeDone: func(s tls.ConnectionState, err error) {
log.Printf("TLSHandshake:: %v elapsed", time.Now() - startTime)
},
}
return httptrace.WithClientTrace(ctx, clientTrace)
}
// WrapRoundTripper returns a new RoundTripper which traces all requests and tls handshakes
// sent over the transport.
func WrapRoundTripper(roundTripper http.RoundTripper) http.RoundTripper {
return &TransportWithTrace{roundTripper, serviceName}
}
// WrapClient modifies the given client's transport to augment it with tracing and returns it.
func WrapClient(c *http.Client) *http.Client {
if c.Transport == nil {
c.Transport = http.DefaultTransport
}
c.Transport = WrapRoundTripper(c.Transport)
return c
}
type ClientTrace struct {
GetConn func(hostPort string)
GotConn func(GotConnInfo)
PutIdleConn func(err error)
GotFirstResponseByte func()
Got100Continue func()
Got1xxResponse func(code int, header textproto.MIMEHeader) error
DNSStart func(DNSStartInfo)
DNSDone func(DNSDoneInfo)
ConnectStart func(network, addr string)
ConnectDone func(network, addr string, err error)
TLSHandshakeStart func()
TLSHandshakeDone func(tls.ConnectionState, error)
WroteHeaderField func(key string, value []string)
WroteHeaders func()
Wait100Continue func()
WroteRequest func(WroteRequestInfo)
}
func main() {
c := http.Client{}
client = WrapClient(c)
req := http.Request{
// fill the required fields
}
res, err := c.Do(req)
if err != nil {
...
}
}
tr := &http.Transport{
MaxIdleConns: 10,
}
client := &http.Client{Transport: tr}
req := http.Request{
// request parameters
}
resp, err := client.Do(req)
if err != nil {
// handle error
}
defer resp.Body.Close()
// read response body
// https://github.com/golang/go/blob/master/src/net/http/transport.go
type Transport struct {
// idle connections
idleConn map[connectMethodKey][]*persistConn
MaxIdleConnsPerHost int
IdleConnTimeout time.Duration
idleLRU connLRU
MaxIdleConns int
// wating requests
idleConnWait map[connectMethodKey]wantConnQueue
connsPerHostWait map[connectMethodKey]wantConnQueue
// # of connections
connsPerHost map[connectMethodKey]int
MaxConnsPerHost int
}
@krapie
Copy link

krapie commented Feb 8, 2024

I love how you explained transport layer's connection in your article.
It was very helpful, and I really approciate it 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment