mirror of
https://github.com/golang/go
synced 2024-11-22 05:54:40 -07:00
net/http: reduce mutex contention
benchmark old ns/op new ns/op delta BenchmarkClientServerParallel 155909 154454 -0.93% BenchmarkClientServerParallel-2 86012 82986 -3.52% BenchmarkClientServerParallel-4 70211 55168 -21.43% BenchmarkClientServerParallel-8 80755 47862 -40.73% BenchmarkClientServerParallel-12 77753 51478 -33.79% BenchmarkClientServerParallel-16 77920 50278 -35.47% The benchmark is https://golang.org/cl/6441134 The machine is 2 x 4 HT cores (16 HW threads total). Fixes #3946. Now contention moves to net.pollServer.AddFD(). R=bradfitz CC=bradfitz, dave, dsymonds, gobot, golang-dev, remyoudompheng https://golang.org/cl/6454142
This commit is contained in:
parent
a8357f0160
commit
20f6a8fdaf
@ -11,8 +11,8 @@ import "time"
|
|||||||
|
|
||||||
func (t *Transport) IdleConnKeysForTesting() (keys []string) {
|
func (t *Transport) IdleConnKeysForTesting() (keys []string) {
|
||||||
keys = make([]string, 0)
|
keys = make([]string, 0)
|
||||||
t.lk.Lock()
|
t.idleLk.Lock()
|
||||||
defer t.lk.Unlock()
|
defer t.idleLk.Unlock()
|
||||||
if t.idleConn == nil {
|
if t.idleConn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -23,8 +23,8 @@ func (t *Transport) IdleConnKeysForTesting() (keys []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
|
func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
|
||||||
t.lk.Lock()
|
t.idleLk.Lock()
|
||||||
defer t.lk.Unlock()
|
defer t.idleLk.Unlock()
|
||||||
if t.idleConn == nil {
|
if t.idleConn == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,9 @@ const DefaultMaxIdleConnsPerHost = 2
|
|||||||
// https, and http proxies (for either http or https with CONNECT).
|
// https, and http proxies (for either http or https with CONNECT).
|
||||||
// Transport can also cache connections for future re-use.
|
// Transport can also cache connections for future re-use.
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
lk sync.Mutex
|
idleLk sync.Mutex
|
||||||
idleConn map[string][]*persistConn
|
idleConn map[string][]*persistConn
|
||||||
|
altLk sync.RWMutex
|
||||||
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
|
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
|
||||||
|
|
||||||
// TODO: tunable on global max cached connections
|
// TODO: tunable on global max cached connections
|
||||||
@ -132,12 +133,12 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
|
|||||||
return nil, errors.New("http: nil Request.Header")
|
return nil, errors.New("http: nil Request.Header")
|
||||||
}
|
}
|
||||||
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
||||||
t.lk.Lock()
|
t.altLk.RLock()
|
||||||
var rt RoundTripper
|
var rt RoundTripper
|
||||||
if t.altProto != nil {
|
if t.altProto != nil {
|
||||||
rt = t.altProto[req.URL.Scheme]
|
rt = t.altProto[req.URL.Scheme]
|
||||||
}
|
}
|
||||||
t.lk.Unlock()
|
t.altLk.RUnlock()
|
||||||
if rt == nil {
|
if rt == nil {
|
||||||
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
|
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
|
||||||
}
|
}
|
||||||
@ -171,8 +172,8 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
|
|||||||
if scheme == "http" || scheme == "https" {
|
if scheme == "http" || scheme == "https" {
|
||||||
panic("protocol " + scheme + " already registered")
|
panic("protocol " + scheme + " already registered")
|
||||||
}
|
}
|
||||||
t.lk.Lock()
|
t.altLk.Lock()
|
||||||
defer t.lk.Unlock()
|
defer t.altLk.Unlock()
|
||||||
if t.altProto == nil {
|
if t.altProto == nil {
|
||||||
t.altProto = make(map[string]RoundTripper)
|
t.altProto = make(map[string]RoundTripper)
|
||||||
}
|
}
|
||||||
@ -187,17 +188,18 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
|
|||||||
// a "keep-alive" state. It does not interrupt any connections currently
|
// a "keep-alive" state. It does not interrupt any connections currently
|
||||||
// in use.
|
// in use.
|
||||||
func (t *Transport) CloseIdleConnections() {
|
func (t *Transport) CloseIdleConnections() {
|
||||||
t.lk.Lock()
|
t.idleLk.Lock()
|
||||||
defer t.lk.Unlock()
|
m := t.idleConn
|
||||||
if t.idleConn == nil {
|
t.idleConn = nil
|
||||||
|
t.idleLk.Unlock()
|
||||||
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, conns := range t.idleConn {
|
for _, conns := range m {
|
||||||
for _, pconn := range conns {
|
for _, pconn := range conns {
|
||||||
pconn.close()
|
pconn.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.idleConn = make(map[string][]*persistConn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -243,8 +245,6 @@ func (cm *connectMethod) proxyAuth() string {
|
|||||||
// If pconn is no longer needed or not in a good state, putIdleConn
|
// If pconn is no longer needed or not in a good state, putIdleConn
|
||||||
// returns false.
|
// returns false.
|
||||||
func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
||||||
t.lk.Lock()
|
|
||||||
defer t.lk.Unlock()
|
|
||||||
if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
|
if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
|
||||||
pconn.close()
|
pconn.close()
|
||||||
return false
|
return false
|
||||||
@ -257,7 +257,12 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
|||||||
if max == 0 {
|
if max == 0 {
|
||||||
max = DefaultMaxIdleConnsPerHost
|
max = DefaultMaxIdleConnsPerHost
|
||||||
}
|
}
|
||||||
|
t.idleLk.Lock()
|
||||||
|
if t.idleConn == nil {
|
||||||
|
t.idleConn = make(map[string][]*persistConn)
|
||||||
|
}
|
||||||
if len(t.idleConn[key]) >= max {
|
if len(t.idleConn[key]) >= max {
|
||||||
|
t.idleLk.Unlock()
|
||||||
pconn.close()
|
pconn.close()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -267,16 +272,17 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.idleConn[key] = append(t.idleConn[key], pconn)
|
t.idleConn[key] = append(t.idleConn[key], pconn)
|
||||||
|
t.idleLk.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
|
func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
|
||||||
t.lk.Lock()
|
|
||||||
defer t.lk.Unlock()
|
|
||||||
if t.idleConn == nil {
|
|
||||||
t.idleConn = make(map[string][]*persistConn)
|
|
||||||
}
|
|
||||||
key := cm.String()
|
key := cm.String()
|
||||||
|
t.idleLk.Lock()
|
||||||
|
defer t.idleLk.Unlock()
|
||||||
|
if t.idleConn == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
pconns, ok := t.idleConn[key]
|
pconns, ok := t.idleConn[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -513,8 +519,9 @@ type persistConn struct {
|
|||||||
|
|
||||||
func (pc *persistConn) isBroken() bool {
|
func (pc *persistConn) isBroken() bool {
|
||||||
pc.lk.Lock()
|
pc.lk.Lock()
|
||||||
defer pc.lk.Unlock()
|
b := pc.broken
|
||||||
return pc.broken
|
pc.lk.Unlock()
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteSideClosedFunc func(error) bool // or nil to use default
|
var remoteSideClosedFunc func(error) bool // or nil to use default
|
||||||
|
Loading…
Reference in New Issue
Block a user