coredns/plugin/forward/connect.go
Miek Gieben 270da82995
plugin/forward: move Dial goroutine out (#1738)
Rework the TestProxyClose - close the proxy in the *same* goroutine
as where we started it. Close channels as long as we don't get dataraces
(this may need another fix).

Move the Dial goroutine out of the connManager - this simplifies things
*and* makes another goroutine go away and removes the need for connErr
channels - can now just be dns.Conn.

Also:

Revert "plugin/forward: gracefull stop (#1701)"
This reverts commit 135377bf77.

Revert "rework TestProxyClose (#1735)"
This reverts commit 9e8893a0b5.
2018-04-26 09:34:58 +01:00

94 lines
2.1 KiB
Go

// Package forward implements a forwarding proxy. It caches an upstream net.Conn for some time, so if the same
// client returns the upstream's Conn will be precached. Depending on how you benchmark this looks to be
// 50% faster than just openening a new connection for every client. It works with UDP and TCP and uses
// inband healthchecking.
package forward
import (
"context"
"io"
"strconv"
"sync/atomic"
"time"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
)
func (p *Proxy) readTimeout() time.Duration {
rtt := time.Duration(atomic.LoadInt64(&p.avgRtt))
if rtt < minTimeout {
return minTimeout
}
if rtt < maxTimeout/2 {
return 2 * rtt
}
return maxTimeout
}
func (p *Proxy) updateRtt(newRtt time.Duration) {
rtt := time.Duration(atomic.LoadInt64(&p.avgRtt))
atomic.AddInt64(&p.avgRtt, int64((newRtt-rtt)/rttCount))
}
func (p *Proxy) connect(ctx context.Context, state request.Request, forceTCP, metric bool) (*dns.Msg, error) {
start := time.Now()
proto := state.Proto()
if forceTCP {
proto = "tcp"
}
conn, cached, err := p.Dial(proto)
if err != nil {
return nil, err
}
// Set buffer size correctly for this client.
conn.UDPSize = uint16(state.Size())
if conn.UDPSize < 512 {
conn.UDPSize = 512
}
conn.SetWriteDeadline(time.Now().Add(timeout))
reqTime := time.Now()
if err := conn.WriteMsg(state.Req); err != nil {
conn.Close() // not giving it back
if err == io.EOF && cached {
return nil, errCachedClosed
}
return nil, err
}
conn.SetReadDeadline(time.Now().Add(p.readTimeout()))
ret, err := conn.ReadMsg()
if err != nil {
p.updateRtt(timeout)
conn.Close() // not giving it back
if err == io.EOF && cached {
return nil, errCachedClosed
}
return ret, err
}
p.updateRtt(time.Since(reqTime))
p.Yield(conn)
if metric {
rc, ok := dns.RcodeToString[ret.Rcode]
if !ok {
rc = strconv.Itoa(ret.Rcode)
}
RequestCount.WithLabelValues(p.addr).Add(1)
RcodeCount.WithLabelValues(rc, p.addr).Add(1)
RequestDuration.WithLabelValues(p.addr).Observe(time.Since(start).Seconds())
}
return ret, nil
}
const rttCount = 4