#363 added a test to prove rr lb works, this doesnt have a lock so it… (#365)

* #363 added a test to prove rr lb works, this doesnt have a lock so it isnt perfect, not sure what the tradeoff is between a lock and a bit of randomness, can change to have a lock anytie

* #363 had a look at other oss roudn robin lbs and they all use a lock so imlemented a lock
This commit is contained in:
Tom Pallister 2018-05-21 18:46:39 +01:00 committed by GitHub
parent f96adf9583
commit d01720c349
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 8 deletions

View File

@ -10,6 +10,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public class RoundRobin : ILoadBalancer public class RoundRobin : ILoadBalancer
{ {
private readonly Func<Task<List<Service>>> _services; private readonly Func<Task<List<Service>>> _services;
private readonly object _lock = new object();
private int _last; private int _last;
@ -21,6 +22,8 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext) public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
{ {
var services = await _services(); var services = await _services();
lock(_lock)
{
if (_last >= services.Count) if (_last >= services.Count)
{ {
_last = 0; _last = 0;
@ -30,6 +33,7 @@ namespace Ocelot.LoadBalancer.LoadBalancers
_last++; _last++;
return new OkResponse<ServiceHostAndPort>(next.HostAndPort); return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
} }
}
public void Release(ServiceHostAndPort hostAndPort) public void Release(ServiceHostAndPort hostAndPort)
{ {

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.LoadBalancer.LoadBalancers;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -26,7 +27,7 @@ namespace Ocelot.AcceptanceTests
} }
[Fact] [Fact]
public void should_load_balance_request() public void should_load_balance_request_with_least_connection()
{ {
var downstreamServiceOneUrl = "http://localhost:50881"; var downstreamServiceOneUrl = "http://localhost:50881";
var downstreamServiceTwoUrl = "http://localhost:50892"; var downstreamServiceTwoUrl = "http://localhost:50892";
@ -41,7 +42,7 @@ namespace Ocelot.AcceptanceTests
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(LeastConnection) },
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
@ -72,6 +73,55 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .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<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(RoundRobin) },
DownstreamHostAndPorts = new List<FileHostAndPort>
{
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) private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
{ {
_counterOne.ShouldBeInRange(bottom, top); _counterOne.ShouldBeInRange(bottom, top);