func (r *Rule) Search(ctx context.Context, req *proto.SearchRequest, rsp *proto.SearchResponse) error { if req.Limit <= 0 { req.Limit = 10 } if req.Offset <= 0 { req.Offset = 0 } rules, err := rule.Search(req.Service, req.Version, req.Limit, req.Offset) if err != nil { return errors.InternalServerError("go.micro.srv.router.Rule.Search", err.Error()) } rsp.Rules = rules return nil }
func (r *router) Select(service string) ([]*proto.Service, error) { if len(service) == 0 { return nil, errors.New("invalid service") } // TODO: cache records or watch for updates services, err := r.r.GetService(service) if err != nil { return nil, err } srvLen := len(services) if srvLen == 0 { return nil, selector.ErrNotFound } // grab dynamic runtime labels // argh all the labels // bad bad bad, don't use 1000 // TODO: fix. also cache labels, err := label.Search(service, "", 1000, 0) if err != nil { return nil, err } // grab manually defined rules // these are overrides to load balancing // TODO: cache. also fix rules, err := rule.Search(service, "", 1000, 0) if err != nil { return nil, err } // TODO: use stats to assign weights to nodes // rather than just arbitrary pointer selection // get the pointer and increment it r.mtx.Lock() pointer := r.pointers[service] pointer++ r.pointers[service] = pointer r.mtx.Unlock() servs := make([]*proto.Service, srvLen) // create starting point based on pointer and length of services i := pointer % srvLen // iterate through the length of the services for j := 0; j < srvLen; j++ { // if the pointer has dropped past length rotate back through if i >= srvLen { i = 0 } // apply the label label.Apply(labels, services[i]) // save the service, pass pointer toProto so it can rotate too servs[j] = toProto(services[i], pointer) i++ } // we've load balanced the nodes // now lets strip based on manual overrides // TODO: accept label overrides // Rules: // service A version latest weight 0 priority 0 // service A version latest weight 100 priority 0 key=foo value=bar // if a Select provides the label foo=bar then we'll return it // even where previously the weight was set to 0 var final []*proto.Service // TODO: should be provided as params to this method userSelectLabels := map[string]string{} for _, service := range servs { if s := rule.Apply(rules, service, userSelectLabels); s != nil { final = append(final, s) } } return final, nil }