github.com/annwntech/go-micro/v2@v2.9.5/sync/etcd/etcd.go (about) 1 // Package etcd is an etcd implementation of lock 2 package etcd 3 4 import ( 5 "context" 6 "errors" 7 "log" 8 "path" 9 "strings" 10 gosync "sync" 11 12 "github.com/annwntech/go-micro/v2/sync" 13 client "github.com/coreos/etcd/clientv3" 14 cc "github.com/coreos/etcd/clientv3/concurrency" 15 ) 16 17 type etcdSync struct { 18 options sync.Options 19 path string 20 client *client.Client 21 22 mtx gosync.Mutex 23 locks map[string]*etcdLock 24 } 25 26 type etcdLock struct { 27 s *cc.Session 28 m *cc.Mutex 29 } 30 31 type etcdLeader struct { 32 opts sync.LeaderOptions 33 s *cc.Session 34 e *cc.Election 35 id string 36 } 37 38 func (e *etcdSync) Leader(id string, opts ...sync.LeaderOption) (sync.Leader, error) { 39 var options sync.LeaderOptions 40 for _, o := range opts { 41 o(&options) 42 } 43 44 // make path 45 path := path.Join(e.path, strings.ReplaceAll(e.options.Prefix+id, "/", "-")) 46 47 s, err := cc.NewSession(e.client) 48 if err != nil { 49 return nil, err 50 } 51 52 l := cc.NewElection(s, path) 53 54 if err := l.Campaign(context.TODO(), id); err != nil { 55 return nil, err 56 } 57 58 return &etcdLeader{ 59 opts: options, 60 e: l, 61 id: id, 62 }, nil 63 } 64 65 func (e *etcdLeader) Status() chan bool { 66 ch := make(chan bool, 1) 67 ech := e.e.Observe(context.Background()) 68 69 go func() { 70 for r := range ech { 71 if string(r.Kvs[0].Value) != e.id { 72 ch <- true 73 close(ch) 74 return 75 } 76 } 77 }() 78 79 return ch 80 } 81 82 func (e *etcdLeader) Resign() error { 83 return e.e.Resign(context.Background()) 84 } 85 86 func (e *etcdSync) Init(opts ...sync.Option) error { 87 for _, o := range opts { 88 o(&e.options) 89 } 90 return nil 91 } 92 93 func (e *etcdSync) Options() sync.Options { 94 return e.options 95 } 96 97 func (e *etcdSync) Lock(id string, opts ...sync.LockOption) error { 98 var options sync.LockOptions 99 for _, o := range opts { 100 o(&options) 101 } 102 103 // make path 104 path := path.Join(e.path, strings.ReplaceAll(e.options.Prefix+id, "/", "-")) 105 106 var sopts []cc.SessionOption 107 if options.TTL > 0 { 108 sopts = append(sopts, cc.WithTTL(int(options.TTL.Seconds()))) 109 } 110 111 s, err := cc.NewSession(e.client, sopts...) 112 if err != nil { 113 return err 114 } 115 116 m := cc.NewMutex(s, path) 117 118 if err := m.Lock(context.TODO()); err != nil { 119 return err 120 } 121 122 e.mtx.Lock() 123 e.locks[id] = &etcdLock{ 124 s: s, 125 m: m, 126 } 127 e.mtx.Unlock() 128 return nil 129 } 130 131 func (e *etcdSync) Unlock(id string) error { 132 e.mtx.Lock() 133 defer e.mtx.Unlock() 134 v, ok := e.locks[id] 135 if !ok { 136 return errors.New("lock not found") 137 } 138 err := v.m.Unlock(context.Background()) 139 delete(e.locks, id) 140 return err 141 } 142 143 func (e *etcdSync) String() string { 144 return "etcd" 145 } 146 147 func NewSync(opts ...sync.Option) sync.Sync { 148 var options sync.Options 149 for _, o := range opts { 150 o(&options) 151 } 152 153 var endpoints []string 154 155 for _, addr := range options.Nodes { 156 if len(addr) > 0 { 157 endpoints = append(endpoints, addr) 158 } 159 } 160 161 if len(endpoints) == 0 { 162 endpoints = []string{"http://127.0.0.1:2379"} 163 } 164 165 // TODO: parse addresses 166 c, err := client.New(client.Config{ 167 Endpoints: endpoints, 168 }) 169 if err != nil { 170 log.Fatal(err) 171 } 172 173 return &etcdSync{ 174 path: "/micro/sync", 175 client: c, 176 options: options, 177 locks: make(map[string]*etcdLock), 178 } 179 }