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"
|
6 | |
"time"
|
7 | 9 |
)
|
8 | 10 |
|
9 | 11 |
const (
|
10 | |
MinHeartBeatTime = time.Millisecond * 500
|
|
12 |
minHeartBeatTime = time.Millisecond * 500
|
11 | 13 |
)
|
12 | 14 |
|
13 | 15 |
// Registrar registers service instance liveness information to etcd.
|
|
16 | 18 |
service Service
|
17 | 19 |
logger log.Logger
|
18 | 20 |
quit chan struct{}
|
|
21 |
sync.Mutex
|
19 | 22 |
}
|
20 | 23 |
|
21 | 24 |
// Service holds the instance identifying data you want to publish to etcd. Key
|
|
28 | 31 |
DeleteOptions *etcd.DeleteOptions
|
29 | 32 |
}
|
30 | 33 |
|
31 | |
// TTLOption allow setting a key with a TTL, and regularly refreshes the lease with a goroutine
|
|
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.
|
32 | 36 |
type TTLOption struct {
|
33 | |
Heartbeat time.Duration
|
34 | |
TTL time.Duration
|
|
37 |
heartbeat time.Duration // e.g. time.Second * 3
|
|
38 |
ttl time.Duration // e.g. time.Second * 10
|
35 | 39 |
}
|
36 | 40 |
|
37 | |
// NewTTLOption returns a TTLOption
|
|
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.
|
38 | 46 |
func NewTTLOption(heartbeat, ttl time.Duration) *TTLOption {
|
39 | |
if heartbeat <= MinHeartBeatTime {
|
40 | |
heartbeat = MinHeartBeatTime
|
|
47 |
if heartbeat <= minHeartBeatTime {
|
|
48 |
heartbeat = minHeartBeatTime
|
41 | 49 |
}
|
42 | 50 |
if ttl <= heartbeat {
|
43 | 51 |
ttl = heartbeat * 3
|
44 | 52 |
}
|
45 | |
return &TTLOption{heartbeat, ttl}
|
|
53 |
return &TTLOption{
|
|
54 |
heartbeat: heartbeat,
|
|
55 |
ttl: ttl,
|
|
56 |
}
|
46 | 57 |
}
|
47 | 58 |
|
48 | 59 |
// NewRegistrar returns a etcd Registrar acting on the provided catalog
|
|
66 | 77 |
} else {
|
67 | 78 |
r.logger.Log("action", "register")
|
68 | 79 |
}
|
69 | |
if r.service.TTL == nil {
|
70 | |
return
|
|
80 |
if r.service.TTL != nil {
|
|
81 |
go r.loop()
|
71 | 82 |
}
|
|
83 |
}
|
|
84 |
|
|
85 |
func (r *Registrar) loop() {
|
|
86 |
r.Lock()
|
72 | 87 |
r.quit = make(chan struct{})
|
73 | |
go func() {
|
74 | |
for {
|
75 | |
select {
|
76 | |
case <-r.quit:
|
77 | |
return
|
78 | |
case <-time.After(r.service.TTL.Heartbeat):
|
79 | |
if err := r.client.Register(r.service); err != nil {
|
80 | |
r.logger.Log("err", err)
|
81 | |
}
|
|
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)
|
82 | 100 |
}
|
83 | 101 |
}
|
84 | |
}()
|
|
102 |
}
|
85 | 103 |
}
|
86 | 104 |
|
87 | 105 |
// Deregister implements the sd.Registrar interface. Call it when you want your
|
|
92 | 110 |
} else {
|
93 | 111 |
r.logger.Log("action", "deregister")
|
94 | 112 |
}
|
|
113 |
r.Lock()
|
|
114 |
defer r.Unlock()
|
95 | 115 |
if r.quit != nil {
|
96 | 116 |
close(r.quit)
|
|
117 |
r.quit = nil
|
97 | 118 |
}
|
98 | 119 |
}
|