예제 #1
0
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            foreach (var endpoint in availableEndpoints)
            {
                if (_quanta.TryGetValue(endpoint, out var quantum))
                {
                    if (!_counters.TryGetValue(endpoint, out var counter))
                    {
                        counter = 0;
                    }

                    if (counter < quantum)
                    {
                        _counters[endpoint] = counter + 1;

                        // reset all counters if we're at the end
                        if (endpoint == availableEndpoints.LastOrDefault() && counter == quantum - 1)
                        {
                            _counters.Clear();
                        }

                        return(endpoint);
                    }
                }
            }

            return(null);
        }
예제 #2
0
        public void PickEndpoint_DeficitRoundRobin_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var first        = new EndpointInfo("1");
            var second       = new EndpointInfo("2");
            var third        = new EndpointInfo("3");
            var endpoints    = new[]
            {
                first,
                second,
                third
            };

            var quanta = new Dictionary <EndpointInfo, int>
            {
                [first]  = 1,
                [second] = 2,
                [third]  = 3
            };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.DeficitRoundRobin,
                                                                        deficitRoundRobinQuanta: quanta);

            // choose first X 1
            var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);

            Assert.Equal(first, next);

            // choose second X 2
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(second, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(second, next);

            // choose third X 3
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);

            // choose first X 1
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(first, next);

            // choose second X 2
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(second, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(second, next);

            // choose third X 3
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);
            next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
            Assert.Equal(third, next);
        }
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            var matching = _selector(availableEndpoints);

            return(ThreadLocalRandom.Current.Next(0, 101) <= _variation
                ? _backingLoadBalancingStrategy.Balance(matching.ToList().AsReadOnly(), loadBalancingOptions)
                : _backingLoadBalancingStrategy.Balance(availableEndpoints.Except(matching).ToList().AsReadOnly(), loadBalancingOptions));
        }
예제 #4
0
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            var exceptPreferred = availableEndpoints
                                  .Except(Enumerable.Repeat(_preferredEndpoint, 1))
                                  .ToList();

            return(_isAvailable(_preferredEndpoint)
                ? _preferredEndpoint
                : _fallBackLoadBalancingStrategy.Balance(exceptPreferred.ToList().AsReadOnly(), loadBalancingOptions));
        }
예제 #5
0
        public void PickDestination_WithoutDestinations_Null()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new DestinationInfo[0];
            var options      = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1));

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Null(result);
        }
예제 #6
0
        public void PickEndpoint_WithoutEndpoints_Null()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new EndpointInfo[0];
            var options      = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1));

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Null(result);
        }
예제 #7
0
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            // Pick two, and then return the least busy. This avoids the effort of searching the whole list, but
            // still avoids overloading a single endpoint.
            var random1        = _randomFactory.CreateRandomInstance();
            var firstEndpoint  = availableEndpoints[random1.Next(availableEndpoints.Count)];
            var secondEndpoint = availableEndpoints[random1.Next(availableEndpoints.Count)];

            return(firstEndpoint.ConcurrencyCounter.Value <= secondEndpoint.ConcurrencyCounter.Value
                ? firstEndpoint
                : secondEndpoint);
        }
예제 #8
0
        public void PickEndpoint_SingleEndpoints_ShortCircuit()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1));

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Equal(result, endpoints[0]);
        }
예제 #9
0
        public void PickDestination_SingleDestinations_ShortCircuit()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1));

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(destinations[0], result);
        }
예제 #10
0
        public void PickEndpoint_FirstWithoutEndpoints_Works()
        {
            // Arrange
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new EndpointInfo[0];
            var options      = new BackendConfig.BackendLoadBalancingOptions(BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode.First);

            // Act
            var result = loadBalancer.PickEndpoint(endpoints, endpoints, in options);

            // Assert
            result.Should().BeNull();
        }
예제 #11
0
        public void PickDestination_FirstWithDestinations_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.First);

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(destinations[0], result);
        }
예제 #12
0
        public void PickEndpoint_FirstWithEndpoints_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.First);

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Same(endpoints[0], result);
        }
예제 #13
0
        public void PickEndpoint_UnsupportedMode_Throws()
        {
            // Arrange
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new EndpointInfo[0];
            var options      = new BackendConfig.BackendLoadBalancingOptions((BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode)(-1));

            // Act
            Action action = () => loadBalancer.PickEndpoint(endpoints, endpoints, in options);

            // Assert
            action.Should().ThrowExactly <ReverseProxyException>()
            .Which.Message.Should().Be("Load balancing mode '-1' is not supported.");
        }
예제 #14
0
        public void PickEndpoint_UnsupportedMode_Throws()
        {
            // Arrange
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new EndpointInfo[0];
            var options      = new BackendConfig.BackendLoadBalancingOptions((BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode)(-1));

            // Act
            Action action = () => loadBalancer.PickEndpoint(endpoints, in options);

            // Assert
            var ex = Assert.Throws <NotSupportedException>(action);

            Assert.Equal("Load balancing mode '-1' is not supported.", ex.Message);
        }
예제 #15
0
        public void PickDestination_Random_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };

            RandomInstance.Sequence = new[] { 1 };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Random);

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(result, destinations[1]);
        }
예제 #16
0
        public void PickDestination_LeastRequests_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };

            destinations[0].ConcurrencyCounter.Increment();
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.LeastRequests);

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(result, destinations[1]);
        }
예제 #17
0
        public void PickEndpoint_LeastRequests_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };

            endpoints[0].ConcurrencyCounter.Increment();
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.LeastRequests);

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Same(result, endpoints[1]);
        }
예제 #18
0
        public void PickEndpoint_Random_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };

            RandomInstance.Sequence = new[] { 1 };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Random);

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Same(result, endpoints[1]);
        }
예제 #19
0
        public void PickDestination_UnsupportedMode_Throws()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1));

            Action action = () => loadBalancer.PickDestination(destinations, in options);

            // Assert
            var ex = Assert.Throws <NotSupportedException>(action);

            Assert.Equal("Load balancing mode '-1' is not supported.", ex.Message);
        }
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            var leastRequestsEndpoint = availableEndpoints[0];
            var leastRequestsCount    = leastRequestsEndpoint.ConcurrencyCounter.Value;

            for (var i = 1; i < availableEndpoints.Count; i++)
            {
                var endpoint             = availableEndpoints[i];
                var endpointRequestCount = endpoint.ConcurrencyCounter.Value;
                if (endpointRequestCount < leastRequestsCount)
                {
                    leastRequestsEndpoint = endpoint;
                    leastRequestsCount    = endpointRequestCount;
                }
            }
            return(leastRequestsEndpoint);
        }
예제 #21
0
        public void PickEndpoint_PowerOfTwoChoices_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };

            endpoints[0].ConcurrencyCounter.Increment();
            RandomInstance.Sequence = new[] { 1, 0 };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.PowerOfTwoChoices);

            var result = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Same(result, endpoints[1]);
        }
예제 #22
0
        public void PickDestination_PowerOfTwoChoices_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };

            destinations[0].ConcurrencyCounter.Increment();
            RandomInstance.Sequence = new[] { 1, 0 };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.PowerOfTwoChoices);

            var result = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(result, destinations[1]);
        }
예제 #23
0
        public void PickEndpoint_FirstWithEndpoints_Works()
        {
            // Arrange
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };
            var options = new BackendConfig.BackendLoadBalancingOptions(BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode.First);

            // Act
            var result = loadBalancer.PickEndpoint(endpoints, endpoints, in options);

            // Assert
            result.Should().BeSameAs(endpoints[0]);
        }
예제 #24
0
        public void PickEndpoint_Callback_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("1"),
                new EndpointInfo("2"),
                new EndpointInfo("3")
            };
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Callback,
                                                                        (availableEndpoints, _) => availableEndpoints.FirstOrDefault(x => x.EndpointId == "3"));

            // choose third
            var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);

            Assert.NotNull(next);
            Assert.Equal("3", next.EndpointId);
        }
예제 #25
0
        public void PickEndpoint_RoundRobin_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var endpoints    = new[]
            {
                new EndpointInfo("ep1"),
                new EndpointInfo("ep2"),
            };

            endpoints[0].ConcurrencyCounter.Increment();
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.RoundRobin);

            var result0 = loadBalancer.PickEndpoint(endpoints, in options);
            var result1 = loadBalancer.PickEndpoint(endpoints, in options);
            var result2 = loadBalancer.PickEndpoint(endpoints, in options);
            var result3 = loadBalancer.PickEndpoint(endpoints, in options);

            Assert.Same(result0, endpoints[0]);
            Assert.Same(result1, endpoints[1]);
            Assert.Same(result2, endpoints[0]);
            Assert.Same(result3, endpoints[1]);
        }
예제 #26
0
        public void PickDestination_RoundRobin_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var destinations = new[]
            {
                new DestinationInfo("d1"),
                new DestinationInfo("d2"),
            };

            destinations[0].ConcurrencyCounter.Increment();
            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.RoundRobin);

            var result0 = loadBalancer.PickDestination(destinations, in options);
            var result1 = loadBalancer.PickDestination(destinations, in options);
            var result2 = loadBalancer.PickDestination(destinations, in options);
            var result3 = loadBalancer.PickDestination(destinations, in options);

            Assert.Same(result0, destinations[0]);
            Assert.Same(result1, destinations[1]);
            Assert.Same(result2, destinations[0]);
            Assert.Same(result3, destinations[1]);
        }
예제 #27
0
        public void PickEndpoint_TrafficAllocation_Works()
        {
            var loadBalancer = Create <LoadBalancer>();
            var first        = new EndpointInfo("1");
            var second       = new EndpointInfo("2");
            var third        = new EndpointInfo("3");
            var endpoints    = new[]
            {
                first,
                second,
                third
            };

            var variation = .10M;
            var options   = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.TrafficAllocation,
                                                                          trafficAllocationSelector: x => x.Where(item => item == second),
                                                                          trafficAllocationVariation: variation,
                                                                          trafficAllocationBackingLoadBalancingStrategy: () => new RandomLoadBalancingStrategy(new RandomFactory()));

            var       selections = 0;
            const int Iterations = 100 * 1000;

            for (var i = 0; i < Iterations; i++)
            {
                var result = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);
                if (result == second)
                {
                    selections++;
                }
            }

            const int DeviationPercentage = 15;
            var       target    = Iterations / (int)(variation * 100);
            var       deviation = target * DeviationPercentage / 100;

            Assert.InRange(selections, target - deviation, target + deviation);
        }
예제 #28
0
        public void PickEndpoint_FailOver_DoesntFailOver()
        {
            var loadBalancer = Create <LoadBalancer>();
            var first        = new EndpointInfo("1");
            var second       = new EndpointInfo("2");
            var third        = new EndpointInfo("3");
            var endpoints    = new[]
            {
                first,
                second,
                third
            };

            var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.FailOver,
                                                                        failOverPreferredEndpoint: () => first,
                                                                        failOverIsAvailablePredicate: _ => true,
                                                                        failOverFallBackLoadBalancingStrategy: () => new RandomLoadBalancingStrategy(new RandomFactory()));

            // choose first
            var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options);

            Assert.NotNull(next);
            Assert.Equal(first, next);
        }
        public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
        {
            var offset = loadBalancingOptions.RoundRobinState.Increment();

            return(availableEndpoints[offset % availableEndpoints.Count]);
        }
예제 #30
0
 public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions)
 {
     return(availableEndpoints[0]);
 }