diff --git a/cmd/redirectord/redirectord.go b/cmd/redirectord/redirectord.go index bb1cd8baf5fe5b9837545540e7114793f22210a5..92ed0e29d07c4154ee8333f0c593c9cd9fc4734e 100644 --- a/cmd/redirectord/redirectord.go +++ b/cmd/redirectord/redirectord.go @@ -18,7 +18,7 @@ var ( httpPort = flag.Int("http-port", 80, "HTTP port") staticDir = flag.String("static-dir", "/usr/share/autoradio/htdocs/static", "Static content directory") templateDir = flag.String("template-dir", "/usr/share/autoradio/htdocs/templates", "HTML templates directory") - lbPolicy = flag.String("lb-policy", "weighted", "Load balancing policy (weighted, leastloaded)") + lbPolicy = flag.String("lb-policy", "listeners_available,listeners_score,weighted", "Load balancing rules specification (see godoc documentation for details)") // Default DNS TTL (seconds). dnsTtl = 5 diff --git a/fe/loadbalancing.go b/fe/loadbalancing.go index bdcc041250c3883abec37f1c6d29ae597f341cde..d4d49e21d7237772225291bad11ea28c988158c6 100644 --- a/fe/loadbalancing.go +++ b/fe/loadbalancing.go @@ -1,6 +1,7 @@ package fe import ( + "errors" "fmt" "net" "strings" @@ -108,32 +109,45 @@ func (l *autoradioLoadBalancer) Choose(ctx lbv2.RequestContext) *autoradio.NodeS return result.(*lbNode).NodeStatus } +// Parse a string that specifies how to build a LoadBalancer. The +// string should consist of a list of comma-separated tokens, each +// identifying a specific filter or policy. +// +// Some filters will always be included in the resulting LoadBalancer +// and do not need to be specified explicitly (icecastActiveFilter and +// ipProtocolFilter, plus an activeNodesFilter at the end). func parseLoadBalancerSpec(specstr string) (*autoradioLoadBalancer, error) { lb := lbv2.New() lb.AddFilter(newIcecastActiveFilter()) lb.AddFilter(newIpProtocolFilter()) + var policy lbv2.Policy for _, spec := range strings.Split(specstr, ",") { switch spec { - case "ba", "bw_avail", "bandwidth_available": + case "bandwidth_available": lb.AddFilter(lbv2.NewCapacityAvailableFilter(lb.GetPredictor(UTIL_BANDWIDTH))) - case "la", "listeners_available": + case "listeners_available": lb.AddFilter(lbv2.NewCapacityAvailableFilter(lb.GetPredictor(UTIL_LISTENERS))) - case "bw", "bandwidth_weight": + case "bandwidth_score": lb.AddFilter(lbv2.NewCapacityAvailableScorer(lb.GetPredictor(UTIL_BANDWIDTH))) - case "lw", "listeners_weight": + case "listeners_score": lb.AddFilter(lbv2.NewCapacityAvailableScorer(lb.GetPredictor(UTIL_LISTENERS))) case "random": - lb.SetPolicy(lbv2.RandomPolicy) + policy = lbv2.RandomPolicy case "weighted": - lb.SetPolicy(lbv2.WeightedPolicy) + policy = lbv2.WeightedPolicy case "best": - lb.SetPolicy(lbv2.HighestScorePolicy) + policy = lbv2.HighestScorePolicy default: return nil, fmt.Errorf("unknown lb filter spec \"%s\"", spec) } } + if policy == nil { + return nil, errors.New("no lb policy specified") + } + lb.SetPolicy(policy) + lb.AddFilter(lbv2.NewActiveNodesFilter()) return &autoradioLoadBalancer{lb}, nil diff --git a/fe/loadbalancing_test.go b/fe/loadbalancing_test.go index 23f5029a11698cb5618af62a8d5de2d3647cee07..e2d8d89e0b28ef63f35dea3ff11491285a6d0a84 100644 --- a/fe/loadbalancing_test.go +++ b/fe/loadbalancing_test.go @@ -104,13 +104,13 @@ func TestLoadBalancer_Policies(t *testing.T) { } // Weighted should return node1 4 times as often as node2. - runLBTest(t, nodes, nil, "bw,weighted", 1000, map[string]int{"node1": 200, "node2": 800}) + runLBTest(t, nodes, nil, "bandwidth_score,weighted", 1000, map[string]int{"node1": 200, "node2": 800}) // The 'random' policy will ignore the weights. - runLBTest(t, nodes, nil, "bw,random", 1000, map[string]int{"node1": 500, "node2": 500}) + runLBTest(t, nodes, nil, "bandwidth_score,random", 1000, map[string]int{"node1": 500, "node2": 500}) // The 'best' policy will always return node2. - runLBTest(t, nodes, nil, "bw,best", 1000, map[string]int{"node2": 1000}) + runLBTest(t, nodes, nil, "bandwidth_score,best", 1000, map[string]int{"node2": 1000}) } func TestLoadBalancer_PoliciesIgnoreDisabledNodes(t *testing.T) { @@ -127,7 +127,7 @@ func TestLoadBalancer_PoliciesIgnoreDisabledNodes(t *testing.T) { }, } - for _, spec := range []string{"bw_avail,weighted", "bw_avail,random", "bw_avail,best"} { + for _, spec := range []string{"bandwidth_available,weighted", "bandwidth_available,random", "bandwidth_available,best"} { runLBTest(t, nodes, nil, spec, 1000, map[string]int{"node1": 1000}) } }