6 | 6 |
"github.com/go-kit/kit/log"
|
7 | 7 |
)
|
8 | 8 |
|
9 | |
// Dialer dials a network and address. net.Dial is a good default Dialer.
|
|
9 |
// Dialer imitates net.Dial. Dialer is assumed to yield connections that are
|
|
10 |
// safe for use by multiple concurrent goroutines.
|
10 | 11 |
type Dialer func(network, address string) (net.Conn, error)
|
11 | 12 |
|
12 | 13 |
// AfterFunc imitates time.After.
|
13 | 14 |
type AfterFunc func(time.Duration) <-chan time.Time
|
14 | 15 |
|
15 | |
// Manager manages a net.Conn. Clients should take the conn when they want to
|
16 | |
// use it, and put back whatever error they receive from an e.g. Write. When a
|
17 | |
// non-nil error is put, the conn is invalidated and a new conn is established.
|
|
16 |
// Manager manages a net.Conn.
|
|
17 |
//
|
|
18 |
// Clients provide a way to create the connection with a Dialer, network, and
|
|
19 |
// address. Clients should Take the connection when they want to use it, and Put
|
|
20 |
// back whatever error they receive from its use. When a non-nil error is Put,
|
|
21 |
// the connection is invalidated, and a new connection is established.
|
18 | 22 |
// Connection failures are retried after an exponential backoff.
|
19 | 23 |
type Manager struct {
|
20 | |
dial Dialer
|
|
24 |
dialer Dialer
|
21 | 25 |
network string
|
22 | 26 |
address string
|
23 | 27 |
after AfterFunc
|
|
27 | 31 |
putc chan error
|
28 | 32 |
}
|
29 | 33 |
|
|
34 |
// NewManager returns a connection manager using the passed Dialer, network, and
|
|
35 |
// address. The AfterFunc is used to control exponential backoff and retries.
|
|
36 |
// For normal use, pass net.Dial and time.After as the Dialer and AfterFunc
|
|
37 |
// respectively. The logger is used to log errors; pass a log.NopLogger if you
|
|
38 |
// don't care to receive them.
|
30 | 39 |
func NewManager(d Dialer, network, address string, after AfterFunc, logger log.Logger) *Manager {
|
31 | 40 |
m := &Manager{
|
32 | |
dial: d,
|
|
41 |
dialer: d,
|
33 | 42 |
network: network,
|
34 | 43 |
address: address,
|
35 | 44 |
after: after,
|
|
42 | 51 |
return m
|
43 | 52 |
}
|
44 | 53 |
|
|
54 |
// Take yields the current connection. It may be nil.
|
45 | 55 |
func (m *Manager) Take() net.Conn {
|
46 | 56 |
return <-m.takec
|
47 | 57 |
}
|
48 | 58 |
|
|
59 |
// Put accepts an error that came from a previously yielded connection. If the
|
|
60 |
// error is non-nil, the manager will invalidate the current connection and try
|
|
61 |
// to reconnect, with exponential backoff. Putting a nil error is a no-op.
|
49 | 62 |
func (m *Manager) Put(err error) {
|
50 | 63 |
m.putc <- err
|
51 | 64 |
}
|
52 | 65 |
|
53 | 66 |
func (m *Manager) loop() {
|
54 | 67 |
var (
|
55 | |
conn = dial(m.dial, m.network, m.address, m.logger) // may block slightly
|
|
68 |
conn = dial(m.dialer, m.network, m.address, m.logger) // may block slightly
|
56 | 69 |
connc = make(chan net.Conn)
|
57 | 70 |
reconnectc <-chan time.Time // initially nil
|
58 | 71 |
backoff = time.Second
|
|
61 | 74 |
for {
|
62 | 75 |
select {
|
63 | 76 |
case <-reconnectc:
|
64 | |
reconnectc = nil
|
65 | |
go func() { connc <- dial(m.dial, m.network, m.address, m.logger) }()
|
|
77 |
reconnectc = nil // one-shot
|
|
78 |
go func() { connc <- dial(m.dialer, m.network, m.address, m.logger) }()
|
66 | 79 |
|
67 | 80 |
case conn = <-connc:
|
68 | 81 |
if conn == nil {
|
69 | |
backoff = exponential(backoff)
|
70 | |
reconnectc = m.after(backoff)
|
|
82 |
// didn't work
|
|
83 |
backoff = exponential(backoff) // wait longer
|
|
84 |
reconnectc = m.after(backoff) // try again
|
71 | 85 |
} else {
|
72 | |
backoff = time.Second
|
73 | |
reconnectc = nil
|
|
86 |
// worked!
|
|
87 |
backoff = time.Second // reset wait time
|
|
88 |
reconnectc = nil // no retry necessary
|
74 | 89 |
}
|
75 | 90 |
|
76 | 91 |
case m.takec <- conn:
|
77 | |
// might be nil
|
78 | 92 |
|
79 | 93 |
case err := <-m.putc:
|
80 | 94 |
if err != nil && conn != nil {
|
|
90 | 104 |
conn, err := d(network, address)
|
91 | 105 |
if err != nil {
|
92 | 106 |
logger.Log("err", err)
|
93 | |
conn = nil
|
|
107 |
conn = nil // just to be sure
|
94 | 108 |
}
|
95 | 109 |
return conn
|
96 | 110 |
}
|