diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs index c9a63b24..b130f6fe 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs @@ -10,6 +10,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers public class RoundRobin : ILoadBalancer { private readonly Func>> _services; + private readonly object _lock = new object(); private int _last; @@ -21,14 +22,17 @@ namespace Ocelot.LoadBalancer.LoadBalancers public async Task> Lease(DownstreamContext downstreamContext) { var services = await _services(); - if (_last >= services.Count) + lock(_lock) { - _last = 0; - } + if (_last >= services.Count) + { + _last = 0; + } - var next = services[_last]; - _last++; - return new OkResponse(next.HostAndPort); + var next = services[_last]; + _last++; + return new OkResponse(next.HostAndPort); + } } public void Release(ServiceHostAndPort hostAndPort) diff --git a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs index a4ce1767..567271a0 100644 --- a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs +++ b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Ocelot.Configuration.File; +using Ocelot.LoadBalancer.LoadBalancers; using Shouldly; using TestStack.BDDfy; using Xunit; @@ -26,7 +27,7 @@ namespace Ocelot.AcceptanceTests } [Fact] - public void should_load_balance_request() + public void should_load_balance_request_with_least_connection() { var downstreamServiceOneUrl = "http://localhost:50881"; var downstreamServiceTwoUrl = "http://localhost:50892"; @@ -41,7 +42,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(LeastConnection) }, DownstreamHostAndPorts = new List { new FileHostAndPort @@ -72,6 +73,55 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_load_balance_request_with_round_robin() + { + var downstreamPortOne = 51881; + var downstreamPortTwo = 51892; + var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; + var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(RoundRobin) }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + } + } + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) + .BDDfy(); + } + private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top) { _counterOne.ShouldBeInRange(bottom, top);