8 | 8 |
)
|
9 | 9 |
|
10 | 10 |
// Publisher yield endpoints stored in a certain etcd keyspace. Any kind of
|
11 | |
// change in that keyspace is watched and wil update the Publisher endpoints.
|
|
11 |
// change in that keyspace is watched and will update the Publisher endpoints.
|
12 | 12 |
type Publisher struct {
|
13 | |
client Client
|
14 | |
prefix string
|
15 | |
factory loadbalancer.Factory
|
16 | |
logger log.Logger
|
17 | |
endpoints chan []endpoint.Endpoint
|
18 | |
quit chan struct{}
|
|
13 |
client Client
|
|
14 |
prefix string
|
|
15 |
cache *loadbalancer.EndpointCache
|
|
16 |
logger log.Logger
|
|
17 |
quit chan struct{}
|
19 | 18 |
}
|
20 | 19 |
|
21 | 20 |
// NewPublisher returs a etcd publisher. Etcd will start watching the given
|
22 | 21 |
// prefix for changes and update the Publisher endpoints.
|
23 | 22 |
func NewPublisher(c Client, prefix string, f loadbalancer.Factory, logger log.Logger) (*Publisher, error) {
|
24 | |
logger = log.NewContext(logger).With("component", "etcd Publisher")
|
25 | 23 |
p := &Publisher{
|
26 | |
client: c,
|
27 | |
prefix: prefix,
|
28 | |
factory: f,
|
29 | |
logger: logger,
|
30 | |
endpoints: make(chan []endpoint.Endpoint),
|
31 | |
quit: make(chan struct{}),
|
|
24 |
client: c,
|
|
25 |
prefix: prefix,
|
|
26 |
cache: loadbalancer.NewEndpointCache(f, logger),
|
|
27 |
logger: logger,
|
|
28 |
quit: make(chan struct{}),
|
32 | 29 |
}
|
33 | |
entries, err := p.client.GetEntries(prefix)
|
34 | |
if err != nil {
|
35 | |
return nil, err
|
|
30 |
|
|
31 |
instances, err := p.client.GetEntries(p.prefix)
|
|
32 |
if err == nil {
|
|
33 |
logger.Log(p.prefix, len(instances))
|
|
34 |
} else {
|
|
35 |
logger.Log("msg", "failed to retrieve entries", "err", err)
|
36 | 36 |
}
|
37 | |
go p.loop(makeEndpoints(entries, f, logger))
|
|
37 |
p.cache.Replace(instances)
|
|
38 |
|
|
39 |
go p.loop()
|
38 | 40 |
return p, nil
|
39 | 41 |
}
|
40 | 42 |
|
41 | |
func (p *Publisher) loop(endpoints map[string]endpointCloser) {
|
|
43 |
func (p *Publisher) loop() {
|
42 | 44 |
responseChan := make(chan *etcd.Response)
|
43 | 45 |
go p.client.WatchPrefix(p.prefix, responseChan)
|
44 | 46 |
for {
|
45 | 47 |
select {
|
46 | |
case p.endpoints <- flatten(endpoints):
|
47 | |
|
48 | 48 |
case <-responseChan:
|
49 | |
entries, err := p.client.GetEntries(p.prefix)
|
|
49 |
instances, err := p.client.GetEntries(p.prefix)
|
50 | 50 |
if err != nil {
|
51 | 51 |
p.logger.Log("msg", "failed to retrieve entries", "err", err)
|
52 | 52 |
continue
|
53 | 53 |
}
|
54 | |
endpoints = migrate(endpoints, makeEndpoints(entries, p.factory, p.logger))
|
|
54 |
p.cache.Replace(instances)
|
55 | 55 |
|
56 | 56 |
case <-p.quit:
|
57 | 57 |
return
|
|
61 | 61 |
|
62 | 62 |
// Endpoints implements the Publisher interface.
|
63 | 63 |
func (p *Publisher) Endpoints() ([]endpoint.Endpoint, error) {
|
64 | |
select {
|
65 | |
case endpoints := <-p.endpoints:
|
66 | |
return endpoints, nil
|
67 | |
case <-p.quit:
|
68 | |
return nil, loadbalancer.ErrPublisherStopped
|
69 | |
}
|
|
64 |
return p.cache.Endpoints(), nil
|
70 | 65 |
}
|
71 | 66 |
|
72 | |
// Stop terminates the publisher.
|
|
67 |
// Stop terminates the Publisher.
|
73 | 68 |
func (p *Publisher) Stop() {
|
74 | 69 |
close(p.quit)
|
75 | 70 |
}
|
76 | |
|
77 | |
func makeEndpoints(addrs []string, f loadbalancer.Factory, logger log.Logger) map[string]endpointCloser {
|
78 | |
m := make(map[string]endpointCloser, len(addrs))
|
79 | |
for _, addr := range addrs {
|
80 | |
if _, ok := m[addr]; ok {
|
81 | |
continue // duplicate
|
82 | |
}
|
83 | |
endpoint, closer, err := f(addr)
|
84 | |
if err != nil {
|
85 | |
logger.Log("instance", addr, "err", err)
|
86 | |
continue
|
87 | |
}
|
88 | |
m[addr] = endpointCloser{endpoint, closer}
|
89 | |
}
|
90 | |
return m
|
91 | |
}
|
92 | |
|
93 | |
type endpointCloser struct {
|
94 | |
endpoint.Endpoint
|
95 | |
loadbalancer.Closer
|
96 | |
}
|
97 | |
|
98 | |
func migrate(prev, curr map[string]endpointCloser) map[string]endpointCloser {
|
99 | |
for instance, ec := range prev {
|
100 | |
if _, ok := curr[instance]; !ok {
|
101 | |
close(ec.Closer)
|
102 | |
}
|
103 | |
}
|
104 | |
return curr
|
105 | |
}
|
106 | |
|
107 | |
func flatten(m map[string]endpointCloser) []endpoint.Endpoint {
|
108 | |
a := make([]endpoint.Endpoint, 0, len(m))
|
109 | |
for _, ec := range m {
|
110 | |
a = append(a, ec.Endpoint)
|
111 | |
}
|
112 | |
return a
|
113 | |
}
|