add router selector and network defaults
This commit is contained in:
		
							
								
								
									
										153
									
								
								client/selector/router/router.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								client/selector/router/router.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| // Package router is a network/router selector | ||||
| package router | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/micro/go-micro/client/selector" | ||||
| 	"github.com/micro/go-micro/network/router" | ||||
| 	"github.com/micro/go-micro/registry" | ||||
| ) | ||||
|  | ||||
| type routerSelector struct { | ||||
| 	opts selector.Options | ||||
|  | ||||
| 	// the router | ||||
| 	r router.Router | ||||
| } | ||||
|  | ||||
| type routerKey struct{} | ||||
|  | ||||
| func (r *routerSelector) Init(opts ...selector.Option) error { | ||||
| 	// no op | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) Options() selector.Options { | ||||
| 	return r.opts | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { | ||||
| 	// lookup router for routes for the service | ||||
| 	routes, err := r.r.Table().Lookup(router.NewQuery( | ||||
| 		router.QueryDestination(service), | ||||
| 	)) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// no routes return not found error | ||||
| 	if len(routes) == 0 { | ||||
| 		return nil, selector.ErrNotFound | ||||
| 	} | ||||
|  | ||||
| 	// TODO: apply filters by pseudo constructing service | ||||
|  | ||||
| 	// sort the routes based on metric | ||||
| 	sort.Slice(routes, func(i, j int) bool { | ||||
| 		return routes[i].Metric < routes[j].Metric | ||||
| 	}) | ||||
|  | ||||
| 	// roundrobin assuming routes are in metric preference order | ||||
| 	var i int | ||||
| 	var mtx sync.Mutex | ||||
|  | ||||
| 	return func() (*registry.Node, error) { | ||||
| 		// get index and increment counter with every call to next | ||||
| 		mtx.Lock() | ||||
| 		idx := i | ||||
| 		i++ | ||||
| 		mtx.Unlock() | ||||
|  | ||||
| 		// get route based on idx | ||||
| 		route := routes[idx%len(routes)] | ||||
|  | ||||
| 		// defaults to gateway and no port | ||||
| 		address := route.Gateway | ||||
| 		port := 0 | ||||
|  | ||||
| 		// check if its host:port | ||||
| 		host, pr, err := net.SplitHostPort(address) | ||||
| 		if err == nil { | ||||
| 			pp, _ := strconv.Atoi(pr) | ||||
| 			// set port | ||||
| 			port = pp | ||||
| 			// set address | ||||
| 			address = host | ||||
| 		} | ||||
|  | ||||
| 		// return as a node | ||||
| 		return ®istry.Node{ | ||||
| 			// TODO: add id and metadata if we can | ||||
| 			Address: address, | ||||
| 			Port:    port, | ||||
| 		}, nil | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) Mark(service string, node *registry.Node, err error) { | ||||
| 	// TODO: pass back metrics or information to the router | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) Reset(service string) { | ||||
| 	// TODO: reset the metrics or information at the router | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) Close() error { | ||||
| 	// stop the router advertisements | ||||
| 	return r.r.Stop() | ||||
| } | ||||
|  | ||||
| func (r *routerSelector) String() string { | ||||
| 	return "router" | ||||
| } | ||||
|  | ||||
| // NewSelector returns a new router based selector | ||||
| func NewSelector(opts ...selector.Option) selector.Selector { | ||||
| 	options := selector.Options{ | ||||
| 		Context: context.Background(), | ||||
| 	} | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	// set default registry if not set | ||||
| 	if options.Registry == nil { | ||||
| 		options.Registry = registry.DefaultRegistry | ||||
| 	} | ||||
|  | ||||
| 	// try get from the context | ||||
| 	r, ok := options.Context.Value(routerKey{}).(router.Router) | ||||
| 	if !ok { | ||||
| 		// TODO: Use router.DefaultRouter? | ||||
| 		r = router.NewRouter( | ||||
| 			router.Registry(options.Registry), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	// start the router advertisements | ||||
| 	r.Advertise() | ||||
|  | ||||
| 	return &routerSelector{ | ||||
| 		opts: options, | ||||
| 		r:    r, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithRouter sets the router as an option | ||||
| func WithRouter(r router.Router) selector.Option { | ||||
| 	return func(o *selector.Options) { | ||||
| 		if o.Context == nil { | ||||
| 			o.Context = context.Background() | ||||
| 		} | ||||
| 		o.Context = context.WithValue(o.Context, routerKey{}, r) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user