update the gossiper

This commit is contained in:
Asim Aslam 2018-12-06 18:19:05 +00:00
parent 1ed2b589a2
commit b343420af6
6 changed files with 509 additions and 254 deletions

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/micro/cli" "github.com/micro/cli"
"github.com/micro/go-log"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
@ -301,10 +302,15 @@ func (c *cmd) Before(ctx *cli.Context) error {
serverOpts = append(serverOpts, server.Registry(*c.opts.Registry)) serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
clientOpts = append(clientOpts, client.Registry(*c.opts.Registry)) clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
(*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)) if err := (*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)); err != nil {
log.Fatalf("Error configuring registry: %v", err)
}
clientOpts = append(clientOpts, client.Selector(*c.opts.Selector)) clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
(*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)) if err := (*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)); err != nil {
log.Fatalf("Error configuring broker: %v", err)
}
} }
// Set the selector // Set the selector
@ -349,15 +355,21 @@ func (c *cmd) Before(ctx *cli.Context) error {
} }
if len(ctx.String("broker_address")) > 0 { if len(ctx.String("broker_address")) > 0 {
(*c.opts.Broker).Init(broker.Addrs(strings.Split(ctx.String("broker_address"), ",")...)) if err := (*c.opts.Broker).Init(broker.Addrs(strings.Split(ctx.String("broker_address"), ",")...)); err != nil {
log.Fatalf("Error configuring broker: %v", err)
}
} }
if len(ctx.String("registry_address")) > 0 { if len(ctx.String("registry_address")) > 0 {
(*c.opts.Registry).Init(registry.Addrs(strings.Split(ctx.String("registry_address"), ",")...)) if err := (*c.opts.Registry).Init(registry.Addrs(strings.Split(ctx.String("registry_address"), ",")...)); err != nil {
log.Fatalf("Error configuring registry: %v", err)
}
} }
if len(ctx.String("transport_address")) > 0 { if len(ctx.String("transport_address")) > 0 {
(*c.opts.Transport).Init(transport.Addrs(strings.Split(ctx.String("transport_address"), ",")...)) if err := (*c.opts.Transport).Init(transport.Addrs(strings.Split(ctx.String("transport_address"), ",")...)); err != nil {
log.Fatalf("Error configuring transport: %v", err)
}
} }
if len(ctx.String("server_name")) > 0 { if len(ctx.String("server_name")) > 0 {
@ -412,12 +424,16 @@ func (c *cmd) Before(ctx *cli.Context) error {
// We have some command line opts for the server. // We have some command line opts for the server.
// Lets set it up // Lets set it up
if len(serverOpts) > 0 { if len(serverOpts) > 0 {
(*c.opts.Server).Init(serverOpts...) if err := (*c.opts.Server).Init(serverOpts...); err != nil {
log.Fatalf("Error configuring server: %v", err)
}
} }
// Use an init option? // Use an init option?
if len(clientOpts) > 0 { if len(clientOpts) > 0 {
(*c.opts.Client).Init(clientOpts...) if err := (*c.opts.Client).Init(clientOpts...); err != nil {
log.Fatalf("Error configuring client: %v", err)
}
} }
return nil return nil

View File

@ -1,4 +1,4 @@
// Package gossip provides a zero dependency registry using the gossip protocol SWIM // Package Gossip provides a gossip registry based on hashicorp/memberlist
package gossip package gossip
import ( import (
@ -15,22 +15,13 @@ import (
"github.com/micro/go-log" "github.com/micro/go-log"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/gossip/proto" pb "github.com/micro/go-micro/registry/gossip/proto"
"github.com/mitchellh/hashstructure"
) )
type gossipRegistry struct { const (
opts registry.Options addAction = "update"
queue *memberlist.TransmitLimitedQueue delAction = "delete"
memberlist *memberlist.Memberlist syncAction = "sync"
delegate *delegate
sync.RWMutex
services map[string][]*registry.Service
watchers map[string]*watcher
}
var (
// defaults to random port
defaultPort = 0
) )
type broadcast struct { type broadcast struct {
@ -39,8 +30,134 @@ type broadcast struct {
} }
type delegate struct { type delegate struct {
queue *memberlist.TransmitLimitedQueue
updates chan *update
}
type gossipRegistry struct {
queue *memberlist.TransmitLimitedQueue queue *memberlist.TransmitLimitedQueue
registry *gossipRegistry updates chan *update
options registry.Options
member *memberlist.Memberlist
interval time.Duration
sync.RWMutex
services map[string][]*registry.Service
s sync.RWMutex
watchers map[string]chan *registry.Result
}
type update struct {
Update *pb.Update
Service *registry.Service
sync chan *registry.Service
}
var (
// You should change this if using secure
DefaultSecret = []byte("gossip")
ExpiryTick = time.Second * 5
)
func configure(g *gossipRegistry, opts ...registry.Option) error {
// loop through address list and get valid entries
addrs := func(curAddrs []string) []string {
var newAddrs []string
for _, addr := range curAddrs {
if trimAddr := strings.TrimSpace(addr); len(trimAddr) > 0 {
newAddrs = append(newAddrs, trimAddr)
}
}
return newAddrs
}
// current address list
curAddrs := addrs(g.options.Addrs)
// parse options
for _, o := range opts {
o(&g.options)
}
// new address list
newAddrs := addrs(g.options.Addrs)
// no new nodes and existing member. no configure
if (len(newAddrs) == len(curAddrs)) && g.member != nil {
return nil
}
// shutdown old member
if g.member != nil {
g.member.Shutdown()
}
// replace addresses
curAddrs = newAddrs
// create a queue
queue := &memberlist.TransmitLimitedQueue{
NumNodes: func() int {
return len(curAddrs)
},
RetransmitMult: 3,
}
// machine hostname
hostname, _ := os.Hostname()
// create a new default config
c := memberlist.DefaultLocalConfig()
// set bind to random port
c.BindPort = 0
// set the name
c.Name = strings.Join([]string{"micro", hostname, uuid.New().String()}, "-")
// set the delegate
c.Delegate = &delegate{
updates: g.updates,
queue: queue,
}
// log to dev null
c.LogOutput = ioutil.Discard
// set a secret key if secure
if g.options.Secure {
k, ok := g.options.Context.Value(contextSecretKey{}).([]byte)
if !ok {
// use the default secret
k = DefaultSecret
}
c.SecretKey = k
}
// TODO: set advertise addr to advertise behind nat
// create the memberlist
m, err := memberlist.Create(c)
if err != nil {
return err
}
// join the memberlist
if len(curAddrs) > 0 {
_, err := m.Join(curAddrs)
if err != nil {
return err
}
}
// set internals
g.queue = queue
g.member = m
g.interval = c.GossipInterval
log.Logf("Registry Listening on %s", m.LocalNode().Address())
return nil
} }
func (b *broadcast) Invalidates(other memberlist.Broadcast) bool { func (b *broadcast) Invalidates(other memberlist.Broadcast) bool {
@ -91,56 +208,35 @@ func (d *delegate) NotifyMsg(b []byte) {
return return
} }
up := new(pb.Update) go func() {
if err := proto.Unmarshal(b, up); err != nil { up := new(pb.Update)
return if err := proto.Unmarshal(b, up); err != nil {
}
// only process service action
if up.Type != "service" {
return
}
var service *registry.Service
switch up.Metadata["Content-Type"] {
case "application/json":
if err := json.Unmarshal(up.Data, &service); err != nil {
return return
} }
// no other content type
default:
return
}
d.registry.Lock() // only process service action
defer d.registry.Unlock() if up.Type != "service" {
// get existing service
s := d.registry.services[service.Name]
// save update
switch up.Action {
case "update":
d.registry.services[service.Name] = addServices(s, []*registry.Service{service})
case "delete":
services := delServices(s, []*registry.Service{service})
if len(services) == 0 {
delete(d.registry.services, service.Name)
return return
} }
d.registry.services[service.Name] = services
default:
return
}
// notify watchers var service *registry.Service
for _, w := range d.registry.watchers {
select { switch up.Metadata["Content-Type"] {
case w.ch <- &registry.Result{Action: up.Action, Service: service}: case "application/json":
if err := json.Unmarshal(up.Data, &service); err != nil {
return
}
// no other content type
default: default:
return
} }
}
// send update
d.updates <- &update{
Update: up,
Service: service,
}
}()
} }
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte { func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
@ -148,9 +244,25 @@ func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
} }
func (d *delegate) LocalState(join bool) []byte { func (d *delegate) LocalState(join bool) []byte {
d.registry.RLock() if !join {
b, _ := json.Marshal(d.registry.services) return []byte{}
d.registry.RUnlock() }
syncCh := make(chan *registry.Service, 1)
services := map[string][]*registry.Service{}
d.updates <- &update{
Update: &pb.Update{
Action: syncAction,
},
sync: syncCh,
}
for srv := range syncCh {
services[srv.Name] = append(services[srv.Name], srv)
}
b, _ := json.Marshal(services)
return b return b
} }
@ -161,37 +273,164 @@ func (d *delegate) MergeRemoteState(buf []byte, join bool) {
if !join { if !join {
return return
} }
var services map[string][]*registry.Service var services map[string][]*registry.Service
if err := json.Unmarshal(buf, &services); err != nil { if err := json.Unmarshal(buf, &services); err != nil {
return return
} }
d.registry.Lock() for _, service := range services {
for k, v := range services { for _, srv := range service {
d.registry.services[k] = addServices(d.registry.services[k], v) d.updates <- &update{
Update: &pb.Update{Action: addAction},
Service: srv,
sync: nil,
}
}
}
}
func (g *gossipRegistry) publish(action string, services []*registry.Service) {
g.s.RLock()
for _, sub := range g.watchers {
go func(sub chan *registry.Result) {
for _, service := range services {
sub <- &registry.Result{Action: action, Service: service}
}
}(sub)
}
g.s.RUnlock()
}
func (g *gossipRegistry) subscribe() (chan *registry.Result, chan bool) {
next := make(chan *registry.Result, 10)
exit := make(chan bool)
id := uuid.New().String()
g.s.Lock()
g.watchers[id] = next
g.s.Unlock()
go func() {
<-exit
g.s.Lock()
delete(g.watchers, id)
close(next)
g.s.Unlock()
}()
return next, exit
}
func (g *gossipRegistry) run() {
var mtx sync.Mutex
updates := map[uint64]*update{}
// expiry loop
go func() {
t := time.NewTicker(ExpiryTick)
defer t.Stop()
for _ = range t.C {
now := uint64(time.Now().UnixNano())
mtx.Lock()
// process all the updates
for k, v := range updates {
// check if expiry time has passed
if d := (v.Update.Timestamp + v.Update.Expires); d < now {
// delete from records
delete(updates, k)
// set to delete
v.Update.Action = delAction
// fire a new update
g.updates <- v
}
}
mtx.Unlock()
}
}()
// process the updates
for u := range g.updates {
switch u.Update.Action {
case addAction:
g.Lock()
if service, ok := g.services[u.Service.Name]; !ok {
g.services[u.Service.Name] = []*registry.Service{u.Service}
} else {
g.services[u.Service.Name] = addServices(service, []*registry.Service{u.Service})
}
g.Unlock()
// publish update to watchers
go g.publish(addAction, []*registry.Service{u.Service})
// we need to expire the node at some point in the future
if u.Update.Expires > 0 {
// create a hash of this service
if hash, err := hashstructure.Hash(u.Service, nil); err == nil {
mtx.Lock()
updates[hash] = u
mtx.Unlock()
}
}
case delAction:
g.Lock()
if service, ok := g.services[u.Service.Name]; ok {
if services := delServices(service, []*registry.Service{u.Service}); len(services) == 0 {
delete(g.services, u.Service.Name)
} else {
g.services[u.Service.Name] = services
}
}
g.Unlock()
// publish update to watchers
go g.publish(delAction, []*registry.Service{u.Service})
// delete from expiry checks
if hash, err := hashstructure.Hash(u.Service, nil); err == nil {
mtx.Lock()
delete(updates, hash)
mtx.Unlock()
}
case syncAction:
// no sync channel provided
if u.sync == nil {
continue
}
g.RLock()
// push all services through the sync chan
for _, service := range g.services {
for _, srv := range service {
u.sync <- srv
}
// publish to watchers
go g.publish(addAction, service)
}
g.RUnlock()
// close the sync chan
close(u.sync)
}
} }
d.registry.Unlock()
} }
func (g *gossipRegistry) Init(opts ...registry.Option) error { func (g *gossipRegistry) Init(opts ...registry.Option) error {
addrs := g.opts.Addrs return configure(g, opts...)
for _, o := range opts {
o(&g.opts)
}
// if we have memberlist join it
if len(addrs) != len(g.opts.Addrs) {
_, err := g.memberlist.Join(g.opts.Addrs)
if err != nil {
return err
}
}
return nil
} }
func (g *gossipRegistry) Options() registry.Options { func (g *gossipRegistry) Options() registry.Options {
return g.opts return g.options
} }
func (g *gossipRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error { func (g *gossipRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
@ -201,12 +440,22 @@ func (g *gossipRegistry) Register(s *registry.Service, opts ...registry.Register
} }
g.Lock() g.Lock()
g.services[s.Name] = addServices(g.services[s.Name], []*registry.Service{s}) if service, ok := g.services[s.Name]; !ok {
g.services[s.Name] = []*registry.Service{s}
} else {
g.services[s.Name] = addServices(service, []*registry.Service{s})
}
g.Unlock() g.Unlock()
var options registry.RegisterOptions
for _, o := range opts {
o(&options)
}
up := &pb.Update{ up := &pb.Update{
Id: uuid.New().String(), Id: uuid.New().String(),
Timestamp: uint64(time.Now().UnixNano()), Timestamp: uint64(time.Now().UnixNano()),
Expires: uint64(options.TTL.Nanoseconds()),
Action: "update", Action: "update",
Type: "service", Type: "service",
Metadata: map[string]string{ Metadata: map[string]string{
@ -220,6 +469,9 @@ func (g *gossipRegistry) Register(s *registry.Service, opts ...registry.Register
notify: nil, notify: nil,
}) })
// wait
<-time.After(g.interval * 2)
return nil return nil
} }
@ -230,7 +482,13 @@ func (g *gossipRegistry) Deregister(s *registry.Service) error {
} }
g.Lock() g.Lock()
g.services[s.Name] = delServices(g.services[s.Name], []*registry.Service{s}) if service, ok := g.services[s.Name]; ok {
if services := delServices(service, []*registry.Service{s}); len(services) == 0 {
delete(g.services, s.Name)
} else {
g.services[s.Name] = services
}
}
g.Unlock() g.Unlock()
up := &pb.Update{ up := &pb.Update{
@ -249,135 +507,59 @@ func (g *gossipRegistry) Deregister(s *registry.Service) error {
notify: nil, notify: nil,
}) })
// wait
<-time.After(g.interval * 2)
return nil return nil
} }
func (g *gossipRegistry) GetService(name string) ([]*registry.Service, error) { func (g *gossipRegistry) GetService(name string) ([]*registry.Service, error) {
g.RLock() g.RLock()
if s, ok := g.services[name]; ok { service, ok := g.services[name]
service := cp(s)
g.RUnlock()
return service, nil
}
g.RUnlock() g.RUnlock()
return nil, registry.ErrNotFound if !ok {
return nil, registry.ErrNotFound
}
return service, nil
} }
func (g *gossipRegistry) ListServices() ([]*registry.Service, error) { func (g *gossipRegistry) ListServices() ([]*registry.Service, error) {
var services []*registry.Service var services []*registry.Service
g.RLock() g.RLock()
for name, _ := range g.services { for _, service := range g.services {
services = append(services, &registry.Service{Name: name}) services = append(services, service...)
} }
g.RUnlock() g.RUnlock()
return services, nil return services, nil
} }
func (g *gossipRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { func (g *gossipRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
var options registry.WatchOptions n, e := g.subscribe()
for _, o := range opts { return newGossipWatcher(n, e, opts...)
o(&options)
}
// watcher id
id := uuid.New().String()
// create watcher
w := &watcher{
ch: make(chan *registry.Result, 1),
exit: make(chan bool),
id: id,
// filter service
srv: options.Service,
// delete self
fn: func() {
g.Lock()
delete(g.watchers, id)
g.Unlock()
},
}
// save watcher
g.Lock()
g.watchers[w.id] = w
g.Unlock()
return w, nil
} }
func (g *gossipRegistry) String() string { func (g *gossipRegistry) String() string {
return "gossip" return "gossip"
} }
func (g *gossipRegistry) run() error {
hostname, _ := os.Hostname()
// delegates
d := new(delegate)
// create a new default config
c := memberlist.DefaultLocalConfig()
// assign the delegate
c.Delegate = d
// Set the bind port
c.BindPort = defaultPort
// set the name
c.Name = strings.Join([]string{"micro", hostname, uuid.New().String()}, "-")
// log to dev null
c.LogOutput = ioutil.Discard
// TODO: set advertise addr to advertise behind nat
// create the memberlist
m, err := memberlist.Create(c)
if err != nil {
return err
}
// if we have memberlist join it
if len(g.opts.Addrs) > 0 {
_, err := m.Join(g.opts.Addrs)
if err != nil {
return err
}
}
// Set the broadcast limit and number of nodes
d.queue = &memberlist.TransmitLimitedQueue{
NumNodes: func() int {
return m.NumMembers()
},
RetransmitMult: 3,
}
g.queue = d.queue
g.memberlist = m
g.delegate = d
d.registry = g
return nil
}
// NewRegistry returns a new gossip registry
func NewRegistry(opts ...registry.Option) registry.Registry { func NewRegistry(opts ...registry.Option) registry.Registry {
var options registry.Options gossip := &gossipRegistry{
for _, o := range opts { options: registry.Options{},
o(&options) updates: make(chan *update, 100),
}
g := &gossipRegistry{
opts: options,
services: make(map[string][]*registry.Service), services: make(map[string][]*registry.Service),
} watchers: make(map[string]chan *registry.Result),
if err := g.run(); err != nil {
log.Fatal(err)
} }
log.Logf("Registry Listening on %s", g.memberlist.LocalNode().Address()) // configure the gossiper
// return gossip registry if err := configure(gossip, opts...); err != nil {
return g log.Fatal("Error configuring registry: %v", err)
}
// run the updater
go gossip.run()
// wait for setup
<-time.After(gossip.interval * 2)
return gossip
} }

View File

@ -0,0 +1,17 @@
package gossip
import (
"context"
"github.com/micro/go-micro/registry"
)
type contextSecretKey struct{}
// Secret specifies an encryption key. The value should be either
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
func Secret(k []byte) registry.Option {
return func(o *registry.Options) {
o.Context = context.WithValue(o.Context, contextSecretKey{}, k)
}
}

View File

@ -1,20 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/registry/gossip/proto/gossip.proto // source: github.com/micro/go-micro/registry/gossip/proto/gossip.proto
/*
Package gossip is a generated protocol buffer package.
It is generated from these files:
github.com/micro/go-micro/registry/gossip/proto/gossip.proto
It has these top-level messages:
Update
*/
package gossip package gossip
import proto "github.com/golang/protobuf/proto" import (
import fmt "fmt" fmt "fmt"
import math "math" proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
@ -25,28 +18,53 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the // A compilation error at this line likely means your copy of the
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Update is the message broadcast // Update is the message broadcast
type Update struct { type Update struct {
// unique id of update // unique id of update
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// unix nano timestamp of update // unix nano timestamp of update
Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp" json:"timestamp,omitempty"` Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// time to live for entry
Expires uint64 `protobuf:"varint,3,opt,name=expires,proto3" json:"expires,omitempty"`
// type of update; service // type of update; service
Type string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"` Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"`
// what action is taken; add, del, put // what action is taken; add, del, put
Action string `protobuf:"bytes,4,opt,name=action" json:"action,omitempty"` Action string `protobuf:"bytes,5,opt,name=action,proto3" json:"action,omitempty"`
// any other associated metadata about the data // any other associated metadata about the data
Metadata map[string]string `protobuf:"bytes,5,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// the payload data; // the payload data;
Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` Data []byte `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *Update) Reset() { *m = Update{} } func (m *Update) Reset() { *m = Update{} }
func (m *Update) String() string { return proto.CompactTextString(m) } func (m *Update) String() string { return proto.CompactTextString(m) }
func (*Update) ProtoMessage() {} func (*Update) ProtoMessage() {}
func (*Update) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*Update) Descriptor() ([]byte, []int) {
return fileDescriptor_18cba623e76e57f3, []int{0}
}
func (m *Update) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Update.Unmarshal(m, b)
}
func (m *Update) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Update.Marshal(b, m, deterministic)
}
func (m *Update) XXX_Merge(src proto.Message) {
xxx_messageInfo_Update.Merge(m, src)
}
func (m *Update) XXX_Size() int {
return xxx_messageInfo_Update.Size(m)
}
func (m *Update) XXX_DiscardUnknown() {
xxx_messageInfo_Update.DiscardUnknown(m)
}
var xxx_messageInfo_Update proto.InternalMessageInfo
func (m *Update) GetId() string { func (m *Update) GetId() string {
if m != nil { if m != nil {
@ -62,6 +80,13 @@ func (m *Update) GetTimestamp() uint64 {
return 0 return 0
} }
func (m *Update) GetExpires() uint64 {
if m != nil {
return m.Expires
}
return 0
}
func (m *Update) GetType() string { func (m *Update) GetType() string {
if m != nil { if m != nil {
return m.Type return m.Type
@ -92,27 +117,29 @@ func (m *Update) GetData() []byte {
func init() { func init() {
proto.RegisterType((*Update)(nil), "gossip.Update") proto.RegisterType((*Update)(nil), "gossip.Update")
proto.RegisterMapType((map[string]string)(nil), "gossip.Update.MetadataEntry")
} }
func init() { func init() {
proto.RegisterFile("github.com/micro/go-micro/registry/gossip/proto/gossip.proto", fileDescriptor0) proto.RegisterFile("github.com/micro/go-micro/registry/gossip/proto/gossip.proto", fileDescriptor_18cba623e76e57f3)
} }
var fileDescriptor0 = []byte{ var fileDescriptor_18cba623e76e57f3 = []byte{
// 237 bytes of a gzipped FileDescriptorProto // 251 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x8f, 0xcf, 0x4a, 0xc4, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xcf, 0x4a, 0xc4, 0x30,
0x10, 0xc6, 0x49, 0xdb, 0x0d, 0x76, 0xfc, 0x83, 0x0c, 0x22, 0x41, 0xf6, 0x50, 0x3c, 0xf5, 0x62, 0x10, 0x87, 0x69, 0xb6, 0x9b, 0xb5, 0xe3, 0x1f, 0x64, 0x10, 0x09, 0xb2, 0x87, 0xe2, 0xa9, 0x17,
0x0b, 0x7a, 0x59, 0xd4, 0xab, 0x47, 0x2f, 0x01, 0x1f, 0x20, 0xdb, 0x86, 0x1a, 0x34, 0x9b, 0x90, 0x5b, 0xd0, 0xcb, 0xa2, 0x5e, 0x3d, 0x7a, 0x09, 0xf8, 0x00, 0xd9, 0x36, 0xd4, 0xa0, 0xd9, 0x84,
0xce, 0x0a, 0x7d, 0x68, 0xdf, 0x41, 0x9a, 0x04, 0xc5, 0xdb, 0xef, 0x37, 0xf9, 0xc2, 0x7c, 0x03, 0x64, 0x56, 0xec, 0x13, 0xf8, 0xda, 0xb2, 0x69, 0x54, 0xbc, 0x7d, 0xdf, 0xcc, 0x24, 0x99, 0x5f,
0xcf, 0x93, 0xa1, 0xf7, 0xe3, 0xbe, 0x1b, 0x9c, 0xed, 0xad, 0x19, 0x82, 0xeb, 0x27, 0x77, 0x97, 0xe0, 0x71, 0x34, 0xf4, 0xba, 0xdf, 0xb6, 0xbd, 0xb3, 0x9d, 0x35, 0x7d, 0x70, 0xdd, 0xe8, 0x6e,
0x20, 0xe8, 0xc9, 0xcc, 0x14, 0x96, 0x7e, 0x72, 0xf3, 0x6c, 0x7c, 0xef, 0x83, 0x23, 0x97, 0xa5, 0x66, 0x08, 0x7a, 0x34, 0x91, 0xc2, 0xd4, 0x8d, 0x2e, 0x46, 0xe3, 0x3b, 0x1f, 0x1c, 0xb9, 0x2c,
0x8b, 0x82, 0x3c, 0xd9, 0xed, 0x37, 0x03, 0xfe, 0xe6, 0x47, 0x45, 0x1a, 0x2f, 0xa0, 0x30, 0xa3, 0x6d, 0x12, 0xe4, 0xb3, 0x5d, 0x7f, 0x31, 0xe0, 0x2f, 0x7e, 0x50, 0xa4, 0xf1, 0x0c, 0x98, 0x19,
0x60, 0x0d, 0x6b, 0x6b, 0x59, 0x98, 0x11, 0xb7, 0x50, 0x93, 0xb1, 0x7a, 0x26, 0x65, 0xbd, 0x28, 0x44, 0x51, 0x17, 0x4d, 0x25, 0x99, 0x19, 0x70, 0x0d, 0x15, 0x19, 0xab, 0x23, 0x29, 0xeb, 0x05,
0x1a, 0xd6, 0x56, 0xf2, 0x6f, 0x80, 0x08, 0x15, 0x2d, 0x5e, 0x8b, 0x32, 0xe6, 0x23, 0xe3, 0x35, 0xab, 0x8b, 0xa6, 0x94, 0x7f, 0x05, 0x14, 0xb0, 0xd2, 0x9f, 0xde, 0x04, 0x1d, 0xc5, 0x22, 0xf5,
0x70, 0x35, 0x90, 0x71, 0x07, 0x51, 0xc5, 0x69, 0x36, 0xdc, 0xc1, 0x89, 0xd5, 0xa4, 0x46, 0x45, 0x7e, 0x14, 0x11, 0x4a, 0x9a, 0xbc, 0x16, 0x65, 0xba, 0x29, 0x31, 0x5e, 0x02, 0x57, 0x3d, 0x19,
0x4a, 0x6c, 0x9a, 0xb2, 0x3d, 0xbd, 0xdf, 0x76, 0xb9, 0x4d, 0xda, 0xdd, 0xbd, 0xe6, 0xe7, 0x97, 0xb7, 0x13, 0xcb, 0x54, 0xcd, 0x86, 0x1b, 0x38, 0xb2, 0x9a, 0xd4, 0xa0, 0x48, 0x09, 0x5e, 0x2f,
0x03, 0x85, 0x45, 0xfe, 0xa6, 0xd7, 0x2d, 0xf1, 0x17, 0x6f, 0x58, 0x7b, 0x26, 0x23, 0xdf, 0x3c, 0x9a, 0xe3, 0xdb, 0x75, 0x9b, 0xf7, 0x9c, 0xb7, 0x6a, 0x9f, 0x73, 0xfb, 0x69, 0x47, 0x61, 0x92,
0xc1, 0xf9, 0xbf, 0x38, 0x5e, 0x42, 0xf9, 0xa1, 0x97, 0xdc, 0x7c, 0x45, 0xbc, 0x82, 0xcd, 0x97, 0xbf, 0xd3, 0x87, 0x57, 0xd2, 0xa9, 0x55, 0x5d, 0x34, 0x27, 0x32, 0xf1, 0xd5, 0x03, 0x9c, 0xfe,
0xfa, 0x3c, 0xea, 0x58, 0xbb, 0x96, 0x49, 0x1e, 0x8b, 0x1d, 0xdb, 0xf3, 0x78, 0xfe, 0xc3, 0x4f, 0x1b, 0xc7, 0x73, 0x58, 0xbc, 0xe9, 0x29, 0x67, 0x3a, 0x20, 0x5e, 0xc0, 0xf2, 0x43, 0xbd, 0xef,
0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x49, 0xa9, 0xd7, 0x3e, 0x01, 0x00, 0x00, 0x75, 0x0a, 0x54, 0xc9, 0x59, 0xee, 0xd9, 0xa6, 0xd8, 0xf2, 0xf4, 0x31, 0x77, 0xdf, 0x01, 0x00,
0x00, 0xff, 0xff, 0x06, 0x6e, 0x00, 0x3c, 0x58, 0x01, 0x00, 0x00,
} }

View File

@ -8,12 +8,14 @@ message Update {
string id = 1; string id = 1;
// unix nano timestamp of update // unix nano timestamp of update
uint64 timestamp = 2; uint64 timestamp = 2;
// time to live for entry
uint64 expires = 3;
// type of update; service // type of update; service
string type = 3; string type = 4;
// what action is taken; add, del, put // what action is taken; add, del, put
string action = 4; string action = 5;
// any other associated metadata about the data // any other associated metadata about the data
map<string, string> metadata = 5; map<string, string> metadata = 6;
// the payload data; // the payload data;
bytes data = 6; bytes data = 7;
} }

View File

@ -4,37 +4,48 @@ import (
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
) )
type watcher struct { type gossipWatcher struct {
id string wo registry.WatchOptions
srv string next chan *registry.Result
ch chan *registry.Result stop chan bool
exit chan bool
fn func()
} }
func (w *watcher) Next() (*registry.Result, error) { func newGossipWatcher(ch chan *registry.Result, stop chan bool, opts ...registry.WatchOption) (registry.Watcher, error) {
var wo registry.WatchOptions
for _, o := range opts {
o(&wo)
}
return &gossipWatcher{
wo: wo,
next: ch,
stop: stop,
}, nil
}
func (m *gossipWatcher) Next() (*registry.Result, error) {
for { for {
select { select {
case r := <-w.ch: case r, ok := <-m.next:
if r.Service == nil { if !ok {
continue return nil, registry.ErrWatcherStopped
} }
if len(w.srv) > 0 && (r.Service.Name != w.srv) { // check watch options
if len(m.wo.Service) > 0 && r.Service.Name != m.wo.Service {
continue continue
} }
return r, nil return r, nil
case <-w.exit: case <-m.stop:
return nil, registry.ErrWatcherStopped return nil, registry.ErrWatcherStopped
} }
} }
} }
func (w *watcher) Stop() { func (m *gossipWatcher) Stop() {
select { select {
case <-w.exit: case <-m.stop:
return return
default: default:
close(w.exit) close(m.stop)
w.fn()
} }
} }