First commit in strategy rework
This commit is contained in:
		
							
								
								
									
										38
									
								
								selector/cache/cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								selector/cache/cache.go
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,6 @@ package cache | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"math/rand" |  | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -33,10 +32,6 @@ var ( | |||||||
| 	DefaultTTL = time.Minute | 	DefaultTTL = time.Minute | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	rand.Seed(time.Now().UnixNano()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *cacheSelector) quit() bool { | func (c *cacheSelector) quit() bool { | ||||||
| 	select { | 	select { | ||||||
| 	case <-c.exit: | 	case <-c.exit: | ||||||
| @@ -329,7 +324,10 @@ func (c *cacheSelector) Options() selector.Options { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *cacheSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { | func (c *cacheSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { | ||||||
| 	var sopts selector.SelectOptions | 	sopts := selector.SelectOptions{ | ||||||
|  | 		Strategy: c.so.Strategy, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for _, opt := range opts { | 	for _, opt := range opts { | ||||||
| 		opt(&sopts) | 		opt(&sopts) | ||||||
| 	} | 	} | ||||||
| @@ -352,29 +350,7 @@ func (c *cacheSelector) Select(service string, opts ...selector.SelectOption) (s | |||||||
| 		return nil, selector.ErrNotFound | 		return nil, selector.ErrNotFound | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var nodes []*registry.Node | 	return sopts.Strategy(services), nil | ||||||
|  |  | ||||||
| 	for _, service := range services { |  | ||||||
| 		for _, node := range service.Nodes { |  | ||||||
| 			nodes = append(nodes, node) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(nodes) == 0 { |  | ||||||
| 		return nil, selector.ErrNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return func() (*registry.Node, error) { |  | ||||||
| 		i := rand.Int() |  | ||||||
| 		j := i % len(services) |  | ||||||
|  |  | ||||||
| 		if len(services[j].Nodes) == 0 { |  | ||||||
| 			return nil, selector.ErrNotFound |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		k := i % len(services[j].Nodes) |  | ||||||
| 		return services[j].Nodes[k], nil |  | ||||||
| 	}, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *cacheSelector) Mark(service string, node *registry.Node, err error) { | func (c *cacheSelector) Mark(service string, node *registry.Node, err error) { | ||||||
| @@ -407,7 +383,9 @@ func (c *cacheSelector) String() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func NewSelector(opts ...selector.Option) selector.Selector { | func NewSelector(opts ...selector.Option) selector.Selector { | ||||||
| 	var sopts selector.Options | 	sopts := selector.Options{ | ||||||
|  | 		Strategy: selector.Random, | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for _, opt := range opts { | 	for _, opt := range opts { | ||||||
| 		opt(&sopts) | 		opt(&sopts) | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								selector/default.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								selector/default.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | package selector | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type defaultSelector struct { | ||||||
|  | 	so Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	rand.Seed(time.Now().Unix()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Init(opts ...Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&r.so) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Options() Options { | ||||||
|  | 	return r.so | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Select(service string, opts ...SelectOption) (Next, error) { | ||||||
|  | 	sopts := SelectOptions{ | ||||||
|  | 		Strategy: r.so.Strategy, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(&sopts) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// get the service | ||||||
|  | 	services, err := r.so.Registry.GetService(service) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// apply the filters | ||||||
|  | 	for _, filter := range sopts.Filters { | ||||||
|  | 		services = filter(services) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// if there's nothing left, return | ||||||
|  | 	if len(services) == 0 { | ||||||
|  | 		return nil, ErrNotFound | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return sopts.Strategy(services), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Mark(service string, node *registry.Node, err error) { | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Reset(service string) { | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *defaultSelector) String() string { | ||||||
|  | 	return "default" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newDefaultSelector(opts ...Option) Selector { | ||||||
|  | 	sopts := Options{ | ||||||
|  | 		Strategy: Random, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(&sopts) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if sopts.Registry == nil { | ||||||
|  | 		sopts.Registry = registry.DefaultRegistry | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &defaultSelector{sopts} | ||||||
|  | } | ||||||
| @@ -9,15 +9,11 @@ import ( | |||||||
| func TestRandomSelector(t *testing.T) { | func TestRandomSelector(t *testing.T) { | ||||||
| 	counts := map[string]int{} | 	counts := map[string]int{} | ||||||
| 
 | 
 | ||||||
| 	rs := &randomSelector{ | 	rs := newDefaultSelector(Registry(mock.NewRegistry())) | ||||||
| 		so: Options{ |  | ||||||
| 			Registry: mock.NewRegistry(), |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	next, err := rs.Select("foo") | 	next, err := rs.Select("foo") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Unexpected error calling random select: %v", err) | 		t.Errorf("Unexpected error calling default select: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for i := 0; i < 100; i++ { | 	for i := 0; i < 100; i++ { | ||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Registry registry.Registry | 	Registry registry.Registry | ||||||
|  | 	Strategy Strategy | ||||||
|  |  | ||||||
| 	// Other options for implementations of the interface | 	// Other options for implementations of the interface | ||||||
| 	// can be stored in a context | 	// can be stored in a context | ||||||
| @@ -16,6 +17,7 @@ type Options struct { | |||||||
|  |  | ||||||
| type SelectOptions struct { | type SelectOptions struct { | ||||||
| 	Filters  []Filter | 	Filters  []Filter | ||||||
|  | 	Strategy Strategy | ||||||
|  |  | ||||||
| 	// Other options for implementations of the interface | 	// Other options for implementations of the interface | ||||||
| 	// can be stored in a context | 	// can be stored in a context | ||||||
| @@ -35,6 +37,13 @@ func Registry(r registry.Registry) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SetStrategy sets the default strategy for the selector | ||||||
|  | func SetStrategy(fn Strategy) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.Strategy = fn | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithFilter adds a filter function to the list of filters | // WithFilter adds a filter function to the list of filters | ||||||
| // used during the Select call. | // used during the Select call. | ||||||
| func WithFilter(fn ...Filter) SelectOption { | func WithFilter(fn ...Filter) SelectOption { | ||||||
| @@ -42,3 +51,10 @@ func WithFilter(fn ...Filter) SelectOption { | |||||||
| 		o.Filters = append(o.Filters, fn...) | 		o.Filters = append(o.Filters, fn...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Strategy sets the selector strategy | ||||||
|  | func WithStrategy(fn Strategy) SelectOption { | ||||||
|  | 	return func(o *SelectOptions) { | ||||||
|  | 		o.Strategy = fn | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,9 +0,0 @@ | |||||||
| package random |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/selector" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewSelector(opts ...selector.Option) selector.Selector { |  | ||||||
| 	return selector.NewSelector(opts...) |  | ||||||
| } |  | ||||||
| @@ -1,104 +0,0 @@ | |||||||
| package selector |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"math/rand" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type randomSelector struct { |  | ||||||
| 	so Options |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	rand.Seed(time.Now().Unix()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Init(opts ...Option) error { |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&r.so) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Options() Options { |  | ||||||
| 	return r.so |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Select(service string, opts ...SelectOption) (Next, error) { |  | ||||||
| 	var sopts SelectOptions |  | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		opt(&sopts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// get the service |  | ||||||
| 	services, err := r.so.Registry.GetService(service) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// apply the filters |  | ||||||
| 	for _, filter := range sopts.Filters { |  | ||||||
| 		services = filter(services) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// if there's nothing left, return |  | ||||||
| 	if len(services) == 0 { |  | ||||||
| 		return nil, ErrNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var nodes []*registry.Node |  | ||||||
|  |  | ||||||
| 	for _, service := range services { |  | ||||||
| 		for _, node := range service.Nodes { |  | ||||||
| 			nodes = append(nodes, node) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(nodes) == 0 { |  | ||||||
| 		return nil, ErrNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return func() (*registry.Node, error) { |  | ||||||
| 		i := rand.Int() |  | ||||||
| 		j := i % len(services) |  | ||||||
|  |  | ||||||
| 		if len(services[j].Nodes) == 0 { |  | ||||||
| 			return nil, ErrNotFound |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		k := i % len(services[j].Nodes) |  | ||||||
| 		return services[j].Nodes[k], nil |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Mark(service string, node *registry.Node, err error) { |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Reset(service string) { |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) Close() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *randomSelector) String() string { |  | ||||||
| 	return "random" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRandomSelector(opts ...Option) Selector { |  | ||||||
| 	var sopts Options |  | ||||||
|  |  | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		opt(&sopts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if sopts.Registry == nil { |  | ||||||
| 		sopts.Registry = registry.DefaultRegistry |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &randomSelector{sopts} |  | ||||||
| } |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| package roundrobin |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| 	"github.com/micro/go-micro/selector" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type roundRobinSelector struct { |  | ||||||
| 	so selector.Options |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Init(opts ...selector.Option) error { |  | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&r.so) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Options() selector.Options { |  | ||||||
| 	return r.so |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { |  | ||||||
| 	var sopts selector.SelectOptions |  | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		opt(&sopts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// get the service |  | ||||||
| 	services, err := r.so.Registry.GetService(service) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// apply the filters |  | ||||||
| 	for _, filter := range sopts.Filters { |  | ||||||
| 		services = filter(services) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// if there's nothing left, return |  | ||||||
| 	if len(services) == 0 { |  | ||||||
| 		return nil, selector.ErrNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var nodes []*registry.Node |  | ||||||
|  |  | ||||||
| 	for _, service := range services { |  | ||||||
| 		for _, node := range service.Nodes { |  | ||||||
| 			nodes = append(nodes, node) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(nodes) == 0 { |  | ||||||
| 		return nil, selector.ErrNotFound |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var i int |  | ||||||
| 	var mtx sync.Mutex |  | ||||||
|  |  | ||||||
| 	return func() (*registry.Node, error) { |  | ||||||
| 		mtx.Lock() |  | ||||||
| 		defer mtx.Unlock() |  | ||||||
| 		i++ |  | ||||||
| 		return nodes[i%len(nodes)], nil |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Mark(service string, node *registry.Node, err error) { |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Reset(service string) { |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) Close() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *roundRobinSelector) String() string { |  | ||||||
| 	return "roundrobin" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewSelector(opts ...selector.Option) selector.Selector { |  | ||||||
| 	var sopts selector.Options |  | ||||||
|  |  | ||||||
| 	for _, opt := range opts { |  | ||||||
| 		opt(&sopts) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if sopts.Registry == nil { |  | ||||||
| 		sopts.Registry = registry.DefaultRegistry |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &roundRobinSelector{sopts} |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| package roundrobin |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/registry/mock" |  | ||||||
| 	"github.com/micro/go-micro/selector" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestRoundRobinSelector(t *testing.T) { |  | ||||||
| 	counts := map[string]int{} |  | ||||||
|  |  | ||||||
| 	rr := &roundRobinSelector{ |  | ||||||
| 		so: selector.Options{ |  | ||||||
| 			Registry: mock.NewRegistry(), |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	next, err := rr.Select("foo") |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Errorf("Unexpected error calling rr select: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for i := 0; i < 100; i++ { |  | ||||||
| 		node, err := next() |  | ||||||
| 		if err != nil { |  | ||||||
| 			t.Errorf("Expected node err, got err: %v", err) |  | ||||||
| 		} |  | ||||||
| 		counts[node.Id]++ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	t.Logf("Round Robin Counts %v", counts) |  | ||||||
| } |  | ||||||
| @@ -79,19 +79,22 @@ type Selector interface { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Next is a function that returns the next node | // Next is a function that returns the next node | ||||||
| // based on the selector's algorithm | // based on the selector's strategy | ||||||
| type Next func() (*registry.Node, error) | type Next func() (*registry.Node, error) | ||||||
|  |  | ||||||
| // Filter is used to filter a service during the selection process | // Filter is used to filter a service during the selection process | ||||||
| type Filter func([]*registry.Service) []*registry.Service | type Filter func([]*registry.Service) []*registry.Service | ||||||
|  |  | ||||||
|  | // Strategy is a selection strategy e.g random, round robin | ||||||
|  | type Strategy func([]*registry.Service) Next | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultSelector = newRandomSelector() | 	DefaultSelector = newDefaultSelector() | ||||||
|  |  | ||||||
| 	ErrNotFound      = errors.New("not found") | 	ErrNotFound      = errors.New("not found") | ||||||
| 	ErrNoneAvailable = errors.New("none available") | 	ErrNoneAvailable = errors.New("none available") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NewSelector(opts ...Option) Selector { | func NewSelector(opts ...Option) Selector { | ||||||
| 	return newRandomSelector(opts...) | 	return newDefaultSelector(opts...) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								selector/strategy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								selector/strategy.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package selector | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	rand.Seed(time.Now().UnixNano()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Random is a random strategy algorithm for node selection | ||||||
|  | func Random(services []*registry.Service) Next { | ||||||
|  | 	var nodes []*registry.Node | ||||||
|  |  | ||||||
|  | 	for _, service := range services { | ||||||
|  | 		nodes = append(nodes, service.Nodes...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return func() (*registry.Node, error) { | ||||||
|  | 		if len(nodes) == 0 { | ||||||
|  | 			return nil, ErrNotFound | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		i := rand.Int() % len(nodes) | ||||||
|  | 		return nodes[i], nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RoundRobin is a roundrobin strategy algorithm for node selection | ||||||
|  | func RoundRobin(services []*registry.Service) Next { | ||||||
|  | 	var nodes []*registry.Node | ||||||
|  |  | ||||||
|  | 	for _, service := range services { | ||||||
|  | 		nodes = append(nodes, service.Nodes...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var i int | ||||||
|  | 	var mtx sync.Mutex | ||||||
|  |  | ||||||
|  | 	return func() (*registry.Node, error) { | ||||||
|  | 		if len(nodes) == 0 { | ||||||
|  | 			return nil, ErrNotFound | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		mtx.Lock() | ||||||
|  | 		node := nodes[i%len(nodes)] | ||||||
|  | 		i++ | ||||||
|  | 		mtx.Unlock() | ||||||
|  |  | ||||||
|  | 		return node, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								selector/strategy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								selector/strategy_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | package selector | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestStrategies(t *testing.T) { | ||||||
|  | 	testData := []*registry.Service{ | ||||||
|  | 		®istry.Service{ | ||||||
|  | 			Name:    "test1", | ||||||
|  | 			Version: "latest", | ||||||
|  | 			Nodes: []*registry.Node{ | ||||||
|  | 				®istry.Node{ | ||||||
|  | 					Id:      "test1-1", | ||||||
|  | 					Address: "10.0.0.1", | ||||||
|  | 					Port:    1001, | ||||||
|  | 				}, | ||||||
|  | 				®istry.Node{ | ||||||
|  | 					Id:      "test1-2", | ||||||
|  | 					Address: "10.0.0.2", | ||||||
|  | 					Port:    1002, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		®istry.Service{ | ||||||
|  | 			Name:    "test1", | ||||||
|  | 			Version: "default", | ||||||
|  | 			Nodes: []*registry.Node{ | ||||||
|  | 				®istry.Node{ | ||||||
|  | 					Id:      "test1-3", | ||||||
|  | 					Address: "10.0.0.3", | ||||||
|  | 					Port:    1003, | ||||||
|  | 				}, | ||||||
|  | 				®istry.Node{ | ||||||
|  | 					Id:      "test1-4", | ||||||
|  | 					Address: "10.0.0.4", | ||||||
|  | 					Port:    1004, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for name, strategy := range map[string]Strategy{"random": Random, "roundrobin": RoundRobin} { | ||||||
|  | 		next := strategy(testData) | ||||||
|  | 		counts := make(map[string]int) | ||||||
|  |  | ||||||
|  | 		for i := 0; i < 100; i++ { | ||||||
|  | 			node, err := next() | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Fatal(err) | ||||||
|  | 			} | ||||||
|  | 			counts[node.Id]++ | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		t.Logf("%s: %+v\n", name, counts) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user