Add sync => go-sync
This commit is contained in:
158
sync/leader/consul/consul.go
Normal file
158
sync/leader/consul/consul.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/api/watch"
|
||||
"github.com/micro/go-micro/sync/leader"
|
||||
)
|
||||
|
||||
type consulLeader struct {
|
||||
opts leader.Options
|
||||
c *api.Client
|
||||
}
|
||||
|
||||
type consulElected struct {
|
||||
c *api.Client
|
||||
l *api.Lock
|
||||
id string
|
||||
key string
|
||||
opts leader.ElectOptions
|
||||
|
||||
mtx sync.RWMutex
|
||||
rv <-chan struct{}
|
||||
}
|
||||
|
||||
func (c *consulLeader) Elect(id string, opts ...leader.ElectOption) (leader.Elected, error) {
|
||||
var options leader.ElectOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
key := path.Join("micro/leader", c.opts.Group)
|
||||
|
||||
lc, err := c.c.LockOpts(&api.LockOptions{
|
||||
Key: key,
|
||||
Value: []byte(id),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rv, err := lc.Lock(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &consulElected{
|
||||
c: c.c,
|
||||
key: key,
|
||||
rv: rv,
|
||||
id: id,
|
||||
l: lc,
|
||||
opts: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *consulLeader) Follow() chan string {
|
||||
ch := make(chan string, 1)
|
||||
|
||||
key := path.Join("/micro/leader", c.opts.Group)
|
||||
|
||||
p, err := watch.Parse(map[string]interface{}{
|
||||
"type": "key",
|
||||
"key": key,
|
||||
})
|
||||
if err != nil {
|
||||
return ch
|
||||
}
|
||||
p.Handler = func(idx uint64, raw interface{}) {
|
||||
if raw == nil {
|
||||
return // ignore
|
||||
}
|
||||
v, ok := raw.(*api.KVPair)
|
||||
if !ok || v == nil {
|
||||
return // ignore
|
||||
}
|
||||
ch <- string(v.Value)
|
||||
}
|
||||
|
||||
go p.RunWithClientAndLogger(c.c, log.New(os.Stdout, "consul: ", log.Lshortfile))
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *consulLeader) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
func (c *consulElected) Id() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *consulElected) Reelect() error {
|
||||
rv, err := c.l.Lock(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mtx.Lock()
|
||||
c.rv = rv
|
||||
c.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consulElected) Revoked() chan bool {
|
||||
ch := make(chan bool, 1)
|
||||
c.mtx.RLock()
|
||||
rv := c.rv
|
||||
c.mtx.RUnlock()
|
||||
|
||||
go func() {
|
||||
<-rv
|
||||
ch <- true
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *consulElected) Resign() error {
|
||||
return c.l.Unlock()
|
||||
}
|
||||
|
||||
func NewLeader(opts ...leader.Option) leader.Leader {
|
||||
options := leader.Options{
|
||||
Group: "default",
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
config := api.DefaultConfig()
|
||||
|
||||
// set host
|
||||
// config.Host something
|
||||
// check if there are any addrs
|
||||
if len(options.Nodes) > 0 {
|
||||
addr, port, err := net.SplitHostPort(options.Nodes[0])
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
}
|
||||
}
|
||||
|
||||
client, _ := api.NewClient(config)
|
||||
|
||||
return &consulLeader{
|
||||
opts: options,
|
||||
c: client,
|
||||
}
|
||||
}
|
145
sync/leader/etcd/etcd.go
Normal file
145
sync/leader/etcd/etcd.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro/sync/leader"
|
||||
client "go.etcd.io/etcd/clientv3"
|
||||
cc "go.etcd.io/etcd/clientv3/concurrency"
|
||||
)
|
||||
|
||||
type etcdLeader struct {
|
||||
opts leader.Options
|
||||
path string
|
||||
client *client.Client
|
||||
}
|
||||
|
||||
type etcdElected struct {
|
||||
s *cc.Session
|
||||
e *cc.Election
|
||||
id string
|
||||
}
|
||||
|
||||
func (e *etcdLeader) Elect(id string, opts ...leader.ElectOption) (leader.Elected, error) {
|
||||
var options leader.ElectOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// make path
|
||||
path := path.Join(e.path, strings.Replace(id, "/", "-", -1))
|
||||
|
||||
s, err := cc.NewSession(e.client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := cc.NewElection(s, path)
|
||||
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
|
||||
if err := l.Campaign(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &etcdElected{
|
||||
e: l,
|
||||
id: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *etcdLeader) Follow() chan string {
|
||||
ch := make(chan string)
|
||||
|
||||
s, err := cc.NewSession(e.client)
|
||||
if err != nil {
|
||||
return ch
|
||||
}
|
||||
|
||||
l := cc.NewElection(s, e.path)
|
||||
ech := l.Observe(context.Background())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case r, ok := <-ech:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ch <- string(r.Kvs[0].Value)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (e *etcdLeader) String() string {
|
||||
return "etcd"
|
||||
}
|
||||
|
||||
func (e *etcdElected) Reelect() error {
|
||||
ctx, _ := context.WithCancel(context.Background())
|
||||
return e.e.Campaign(ctx, e.id)
|
||||
}
|
||||
|
||||
func (e *etcdElected) Revoked() chan bool {
|
||||
ch := make(chan bool, 1)
|
||||
ech := e.e.Observe(context.Background())
|
||||
|
||||
go func() {
|
||||
for r := range ech {
|
||||
if string(r.Kvs[0].Value) != e.id {
|
||||
ch <- true
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (e *etcdElected) Resign() error {
|
||||
return e.e.Resign(context.Background())
|
||||
}
|
||||
|
||||
func (e *etcdElected) Id() string {
|
||||
return e.id
|
||||
}
|
||||
|
||||
func NewLeader(opts ...leader.Option) leader.Leader {
|
||||
var options leader.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
var endpoints []string
|
||||
|
||||
for _, addr := range options.Nodes {
|
||||
if len(addr) > 0 {
|
||||
endpoints = append(endpoints, addr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = []string{"http://127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
// TODO: parse addresses
|
||||
c, err := client.New(client.Config{
|
||||
Endpoints: endpoints,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &etcdLeader{
|
||||
path: "/micro/leader",
|
||||
client: c,
|
||||
opts: options,
|
||||
}
|
||||
}
|
25
sync/leader/leader.go
Normal file
25
sync/leader/leader.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Package leader provides leader election
|
||||
package leader
|
||||
|
||||
// Leader provides leadership election
|
||||
type Leader interface {
|
||||
// elect leader
|
||||
Elect(id string, opts ...ElectOption) (Elected, error)
|
||||
// follow the leader
|
||||
Follow() chan string
|
||||
}
|
||||
|
||||
type Elected interface {
|
||||
// id of leader
|
||||
Id() string
|
||||
// seek re-election
|
||||
Reelect() error
|
||||
// resign leadership
|
||||
Resign() error
|
||||
// observe leadership revocation
|
||||
Revoked() chan bool
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type ElectOption func(o *ElectOptions)
|
22
sync/leader/options.go
Normal file
22
sync/leader/options.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package leader
|
||||
|
||||
type Options struct {
|
||||
Nodes []string
|
||||
Group string
|
||||
}
|
||||
|
||||
type ElectOptions struct{}
|
||||
|
||||
// Nodes sets the addresses of the underlying systems
|
||||
func Nodes(a ...string) Option {
|
||||
return func(o *Options) {
|
||||
o.Nodes = a
|
||||
}
|
||||
}
|
||||
|
||||
// Group sets the group name for coordinating leadership
|
||||
func Group(g string) Option {
|
||||
return func(o *Options) {
|
||||
o.Group = g
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user