@@ -4,24 +4,68 @@ import (
4
4
"fmt"
5
5
"net"
6
6
"sync"
7
+ "time"
7
8
)
8
9
10
+ // lookupInterval is the interval of retrying DNS lookup for unresolved hosts
11
+ const lookupInterval = time .Minute * 3
12
+
9
13
// DNSHostProvider is the default HostProvider. It currently matches
10
14
// the Java StaticHostProvider, resolving hosts from DNS once during
11
15
// the call to Init. It could be easily extended to re-query DNS
12
16
// periodically or if there is trouble connecting.
13
17
type DNSHostProvider struct {
14
- mu sync.Mutex // Protects everything, so we can add asynchronous updates later.
15
- servers []string
16
- curr int
17
- last int
18
- lookupHost func (string ) ([]string , error ) // Override of net.LookupHost, for testing.
18
+ sleep func (time.Duration ) // Override of time.Sleep, for testing.
19
+
20
+ mu sync.Mutex // Protects everything, so we can add asynchronous updates later.
21
+ servers []string
22
+ unresolvedServers map [string ]struct {}
23
+ curr int
24
+ last int
25
+ lookupHost func (string ) ([]string , error ) // Override of net.LookupHost, for testing.
19
26
}
20
27
21
28
// Init is called first, with the servers specified in the connection
22
29
// string. It uses DNS to look up addresses for each server, then
23
30
// shuffles them all together.
24
31
func (hp * DNSHostProvider ) Init (servers []string ) error {
32
+ if hp .sleep == nil {
33
+ hp .sleep = time .Sleep
34
+ }
35
+ hp .servers = make ([]string , 0 , len (servers ))
36
+ hp .unresolvedServers = make (map [string ]struct {}, len (servers ))
37
+ for _ , server := range servers {
38
+ hp .unresolvedServers [server ] = struct {}{}
39
+ }
40
+
41
+ done , err := hp .lookupUnresolvedServers ()
42
+ if err != nil {
43
+ return err
44
+ }
45
+
46
+ // as long as any host resolved successfully, consider the connection as success
47
+ // but start a lookup loop until all servers are resolved and added to servers list
48
+ if ! done {
49
+ go hp .lookupLoop ()
50
+ }
51
+
52
+ return nil
53
+ }
54
+
55
+ // lookupLoop calls lookupUnresolvedServers in an infinite loop until all hosts are resolved
56
+ // should be called in a separate goroutine
57
+ func (hp * DNSHostProvider ) lookupLoop () {
58
+ for {
59
+ if done , _ := hp .lookupUnresolvedServers (); done {
60
+ break
61
+ }
62
+ hp .sleep (lookupInterval )
63
+ }
64
+ }
65
+
66
+ // lookupUnresolvedServers DNS lookup the hosts that not successfully resolved yet
67
+ // and add them to servers list
68
+ func (hp * DNSHostProvider ) lookupUnresolvedServers () (bool , error ) {
25
69
hp .mu .Lock ()
26
70
defer hp .mu .Unlock ()
27
71
@@ -30,33 +74,37 @@ func (hp *DNSHostProvider) Init(servers []string) error {
30
74
lookupHost = net .LookupHost
31
75
}
32
76
33
- found := []string {}
34
- for _ , server := range servers {
77
+ if len (hp .unresolvedServers ) == 0 {
78
+ return true , nil
79
+ }
80
+
81
+ found := make ([]string , 0 , len (hp .unresolvedServers ))
82
+ for server := range hp .unresolvedServers {
35
83
host , port , err := net .SplitHostPort (server )
36
84
if err != nil {
37
- return err
85
+ return false , err
38
86
}
39
87
addrs , err := lookupHost (host )
40
88
if err != nil {
41
- return err
89
+ continue
42
90
}
91
+ delete (hp .unresolvedServers , server )
43
92
for _ , addr := range addrs {
44
93
found = append (found , net .JoinHostPort (addr , port ))
45
94
}
46
95
}
47
-
48
- if len (found ) == 0 {
49
- return fmt .Errorf ("No hosts found for addresses %q" , servers )
50
- }
51
-
52
96
// Randomize the order of the servers to avoid creating hotspots
53
97
stringShuffle (found )
54
98
55
- hp .servers = found
99
+ hp .servers = append ( hp . servers , found ... )
56
100
hp .curr = - 1
57
101
hp .last = - 1
58
102
59
- return nil
103
+ if len (hp .servers ) == 0 {
104
+ return true , fmt .Errorf ("No hosts found for addresses %q" , hp .servers )
105
+ }
106
+
107
+ return false , nil
60
108
}
61
109
62
110
// Len returns the number of servers available
0 commit comments