0 | 0 |
package etcd
|
1 | 1 |
|
2 | 2 |
import (
|
|
3 |
"sync"
|
|
4 |
"time"
|
|
5 |
|
3 | 6 |
etcd "github.com/coreos/etcd/client"
|
4 | 7 |
|
5 | 8 |
"github.com/go-kit/kit/log"
|
|
9 |
)
|
|
10 |
|
|
11 |
const (
|
|
12 |
minHeartBeatTime = time.Millisecond * 500
|
6 | 13 |
)
|
7 | 14 |
|
8 | 15 |
// Registrar registers service instance liveness information to etcd.
|
|
10 | 17 |
client Client
|
11 | 18 |
service Service
|
12 | 19 |
logger log.Logger
|
|
20 |
quit chan struct{}
|
|
21 |
sync.Mutex
|
13 | 22 |
}
|
14 | 23 |
|
15 | 24 |
// Service holds the instance identifying data you want to publish to etcd. Key
|
|
18 | 27 |
type Service struct {
|
19 | 28 |
Key string // unique key, e.g. "/service/foobar/1.2.3.4:8080"
|
20 | 29 |
Value string // returned to subscribers, e.g. "http://1.2.3.4:8080"
|
|
30 |
TTL *TTLOption
|
21 | 31 |
DeleteOptions *etcd.DeleteOptions
|
|
32 |
}
|
|
33 |
|
|
34 |
// TTLOption allow setting a key with a TTL. This option will be used by a loop
|
|
35 |
// goroutine which regularly refreshes the lease of the key.
|
|
36 |
type TTLOption struct {
|
|
37 |
heartbeat time.Duration // e.g. time.Second * 3
|
|
38 |
ttl time.Duration // e.g. time.Second * 10
|
|
39 |
}
|
|
40 |
|
|
41 |
// NewTTLOption returns a TTLOption that contains proper ttl settings. param
|
|
42 |
// heartbeat is used to refresh lease of the key periodically by a loop goroutine,
|
|
43 |
// its value should be at least 500ms. param ttl definite the lease of the key,
|
|
44 |
// its value should be greater than heartbeat's.
|
|
45 |
// e.g. heartbeat: time.Second * 3, ttl: time.Second * 10.
|
|
46 |
func NewTTLOption(heartbeat, ttl time.Duration) *TTLOption {
|
|
47 |
if heartbeat <= minHeartBeatTime {
|
|
48 |
heartbeat = minHeartBeatTime
|
|
49 |
}
|
|
50 |
if ttl <= heartbeat {
|
|
51 |
ttl = heartbeat * 3
|
|
52 |
}
|
|
53 |
return &TTLOption{
|
|
54 |
heartbeat: heartbeat,
|
|
55 |
ttl: ttl,
|
|
56 |
}
|
22 | 57 |
}
|
23 | 58 |
|
24 | 59 |
// NewRegistrar returns a etcd Registrar acting on the provided catalog
|
|
42 | 77 |
} else {
|
43 | 78 |
r.logger.Log("action", "register")
|
44 | 79 |
}
|
|
80 |
if r.service.TTL != nil {
|
|
81 |
go r.loop()
|
|
82 |
}
|
|
83 |
}
|
|
84 |
|
|
85 |
func (r *Registrar) loop() {
|
|
86 |
r.Lock()
|
|
87 |
r.quit = make(chan struct{})
|
|
88 |
r.Unlock()
|
|
89 |
|
|
90 |
tick := time.NewTicker(r.service.TTL.heartbeat)
|
|
91 |
defer tick.Stop()
|
|
92 |
|
|
93 |
for {
|
|
94 |
select {
|
|
95 |
case <-r.quit:
|
|
96 |
return
|
|
97 |
case <-tick.C:
|
|
98 |
if err := r.client.Register(r.service); err != nil {
|
|
99 |
r.logger.Log("err", err)
|
|
100 |
}
|
|
101 |
}
|
|
102 |
}
|
45 | 103 |
}
|
46 | 104 |
|
47 | 105 |
// Deregister implements the sd.Registrar interface. Call it when you want your
|
|
52 | 110 |
} else {
|
53 | 111 |
r.logger.Log("action", "deregister")
|
54 | 112 |
}
|
|
113 |
r.Lock()
|
|
114 |
defer r.Unlock()
|
|
115 |
if r.quit != nil {
|
|
116 |
close(r.quit)
|
|
117 |
r.quit = nil
|
|
118 |
}
|
55 | 119 |
}
|