refactor naming package
This commit is contained in:
@ -34,115 +34,49 @@
|
|||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
etcdcl "github.com/coreos/etcd/client"
|
etcdcl "github.com/coreos/etcd/client"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/naming"
|
"google.golang.org/grpc/naming"
|
||||||
)
|
)
|
||||||
// update defines an etcd key-value update.
|
|
||||||
type update struct {
|
|
||||||
key, val string
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNode reports the set of changes starting from node recursively.
|
// getNode builds the key-value map starting from node recursively. It returns the
|
||||||
func getNode(node *etcdcl.Node) (updates []*update) {
|
// max etcdcl.Node.ModifiedIndex starting from that node.
|
||||||
for _, v := range node.Nodes {
|
func getNode(node *etcdcl.Node, kv map[string]string) uint64 {
|
||||||
updates = append(updates, getNode(v)...)
|
|
||||||
}
|
|
||||||
if !node.Dir {
|
if !node.Dir {
|
||||||
u := &update{
|
kv[node.Key] = node.Value
|
||||||
key: node.Key,
|
return node.ModifiedIndex
|
||||||
val: node.Value,
|
|
||||||
}
|
|
||||||
updates = []*update{u}
|
|
||||||
}
|
}
|
||||||
return
|
var max uint64
|
||||||
}
|
for _, v := range node.Nodes {
|
||||||
|
i := getNode(v, kv)
|
||||||
type watcher struct {
|
if max < i {
|
||||||
wr etcdcl.Watcher
|
max = i
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
kv map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *watcher) Next() (nu []*naming.Update, _ error) {
|
|
||||||
for {
|
|
||||||
resp, err := w.wr.Next(w.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
updates := getNode(resp.Node)
|
|
||||||
for _, u := range updates {
|
|
||||||
switch resp.Action {
|
|
||||||
case "set":
|
|
||||||
if resp.PrevNode == nil {
|
|
||||||
w.kv[u.key] = u.val
|
|
||||||
nu = append(nu, &naming.Update{
|
|
||||||
Op: naming.Add,
|
|
||||||
Addr: u.val,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
nu = append(nu, &naming.Update{
|
|
||||||
Op: naming.Delete,
|
|
||||||
Addr: w.kv[u.key],
|
|
||||||
})
|
|
||||||
nu = append(nu, &naming.Update{
|
|
||||||
Op: naming.Add,
|
|
||||||
Addr: u.val,
|
|
||||||
})
|
|
||||||
w.kv[u.key] = u.val
|
|
||||||
}
|
|
||||||
case "delete":
|
|
||||||
nu = append(nu, &naming.Update{
|
|
||||||
Op: naming.Delete,
|
|
||||||
Addr: w.kv[u.key],
|
|
||||||
})
|
|
||||||
delete(w.kv, u.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(nu) > 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nu, nil
|
return max
|
||||||
}
|
|
||||||
|
|
||||||
func (w *watcher) Stop() {
|
|
||||||
w.cancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolver struct {
|
type resolver struct {
|
||||||
kapi etcdcl.KeysAPI
|
kapi etcdcl.KeysAPI
|
||||||
kv map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolver) NewWatcher(target string) naming.Watcher {
|
func (r *resolver) Resolve(target string) (naming.Watcher, error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
w := &watcher{
|
|
||||||
wr: r.kapi.Watcher(target, &etcdcl.WatcherOptions{Recursive: true}),
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
|
||||||
for k, v := range r.kv {
|
|
||||||
w.kv[k] = v
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *resolver) Resolve(target string) (nu []*naming.Update, _ error) {
|
|
||||||
resp, err := r.kapi.Get(context.Background(), target, &etcdcl.GetOptions{Recursive: true})
|
resp, err := r.kapi.Get(context.Background(), target, &etcdcl.GetOptions{Recursive: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
updates := getNode(resp.Node)
|
kv := make(map[string]string)
|
||||||
for _, u := range updates {
|
// Record the index in order to avoid missing updates between Get returning and
|
||||||
r.kv[u.key] = u.val
|
// watch starting.
|
||||||
nu = append(nu, &naming.Update{
|
index := getNode(resp.Node, kv)
|
||||||
Op: naming.Add,
|
return &watcher{
|
||||||
Addr: u.val,
|
wr: r.kapi.Watcher(target, &etcdcl.WatcherOptions{
|
||||||
})
|
AfterIndex: index,
|
||||||
}
|
Recursive: true}),
|
||||||
return nu, nil
|
kv: kv,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResolver creates an etcd-based naming.Resolver.
|
// NewResolver creates an etcd-based naming.Resolver.
|
||||||
@ -153,6 +87,68 @@ func NewResolver(cfg etcdcl.Config) (naming.Resolver, error) {
|
|||||||
}
|
}
|
||||||
return &resolver{
|
return &resolver{
|
||||||
kapi: etcdcl.NewKeysAPI(c),
|
kapi: etcdcl.NewKeysAPI(c),
|
||||||
kv: make(map[string]string),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type watcher struct {
|
||||||
|
wr etcdcl.Watcher
|
||||||
|
kv map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
|
func (w *watcher) Next(ctx context.Context) (nu []*naming.Update, err error) {
|
||||||
|
once.Do(func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = ctx.Err()
|
||||||
|
default:
|
||||||
|
for _, v := range w.kv {
|
||||||
|
nu = append(nu, &naming.Update{
|
||||||
|
Op: naming.Add,
|
||||||
|
Addr: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if len(nu) > 0 || err != nil {
|
||||||
|
// once.Do ran. Return directly.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
resp, err := w.wr.Next(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.Node.Dir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch resp.Action {
|
||||||
|
case "set":
|
||||||
|
if resp.PrevNode == nil {
|
||||||
|
nu = append(nu, &naming.Update{
|
||||||
|
Op: naming.Add,
|
||||||
|
Addr: resp.Node.Value,
|
||||||
|
})
|
||||||
|
w.kv[resp.Node.Key] = resp.Node.Value
|
||||||
|
} else {
|
||||||
|
nu = append(nu, &naming.Update{
|
||||||
|
Op: naming.Delete,
|
||||||
|
Addr: w.kv[resp.Node.Key],
|
||||||
|
})
|
||||||
|
nu = append(nu, &naming.Update{
|
||||||
|
Op: naming.Add,
|
||||||
|
Addr: resp.Node.Value,
|
||||||
|
})
|
||||||
|
w.kv[resp.Node.Key] = resp.Node.Value
|
||||||
|
}
|
||||||
|
case "delete":
|
||||||
|
nu = append(nu, &naming.Update{
|
||||||
|
Op: naming.Delete,
|
||||||
|
Addr: resp.Node.Value,
|
||||||
|
})
|
||||||
|
delete(w.kv, resp.Node.Key)
|
||||||
|
}
|
||||||
|
return nu, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,40 +35,41 @@
|
|||||||
// The interface is EXPERIMENTAL and may be suject to change.
|
// The interface is EXPERIMENTAL and may be suject to change.
|
||||||
package naming
|
package naming
|
||||||
|
|
||||||
// OP defines the corresponding operations for a name resolution change.
|
import (
|
||||||
type OP uint8
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Operation defines the corresponding operations for a name resolution change.
|
||||||
|
type Operation uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Add indicates a new address is added.
|
// Add indicates a new address is added.
|
||||||
Add = iota
|
Add Operation = iota
|
||||||
// Delete indicates an exisiting address is deleted.
|
// Delete indicates an exisiting address is deleted.
|
||||||
Delete
|
Delete
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceConfig interface{}
|
// Update defines a name resolution update. Notice that it is not valid having both
|
||||||
|
// empty string Addr and nil Metadata in an Update.
|
||||||
// Update defines a name resolution change.
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
// Op indicates the operation of the update.
|
// Op indicates the operation of the update.
|
||||||
Op OP
|
Op Operation
|
||||||
Addr string
|
// Addr is the updated address. It is empty string if there is no address update.
|
||||||
Config ServiceConfig
|
Addr string
|
||||||
|
// Metadata is the updated metadata. It is nil if there is no metadata update.
|
||||||
|
// Metadata is not required for a custom naming implementation.
|
||||||
|
Metadata interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver does one-shot name resolution and creates a Watcher to
|
// Resolver creates a Watcher for a target to track its resolution changes.
|
||||||
// watch the future updates.
|
|
||||||
type Resolver interface {
|
type Resolver interface {
|
||||||
// Resolve returns the name resolution results.
|
// Resolve creates a Watcher for target.
|
||||||
Resolve(target string) ([]*Update, error)
|
Resolve(target string) (Watcher, error)
|
||||||
// NewWatcher creates a Watcher to watch the changes on target.
|
|
||||||
NewWatcher(target string) Watcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watcher watches the updates for a particular target.
|
// Watcher watches for the updates on the specified target.
|
||||||
type Watcher interface {
|
type Watcher interface {
|
||||||
// Next blocks until an update or error happens. It may return one or more
|
// Next blocks until an update or error happens. It may return one or more
|
||||||
// updates.
|
// updates. The first call should get the full set of the results.
|
||||||
Next() ([]*Update, error)
|
Next(ctx context.Context) ([]*Update, error)
|
||||||
// Stop stops the Watcher.
|
|
||||||
Stop()
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user