plying around with service providers

This commit is contained in:
Tom Gardham-Pallister 2017-02-01 19:34:55 +00:00
parent 0e92976df8
commit 24dbb958e3
12 changed files with 314 additions and 46 deletions

View File

@ -7,6 +7,7 @@ using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Validator;
using Ocelot.Responses;
using Ocelot.ServiceDiscovery;
using Ocelot.Utilities;
using Ocelot.Values;
@ -104,7 +105,22 @@ namespace Ocelot.Configuration.Creator
//ideal world we would get the host and port, then make the request using it, then release the connection to the lb
Func<HostAndPort> downstreamHostAndPortFunc = () => new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort);
Func<HostAndPort> downstreamHostAndPortFunc = () => {
//service provider factory takes the reRoute
//can return no service provider (just use ocelot config)
//can return consol service provider
//returns a service provider
//we call get on the service provider
//could reutrn services from consol or just configuration.json
//this returns a list of services and we take the first one
var hostAndPort = new HostAndPort(reRoute.DownstreamHost.Trim('/'), reRoute.DownstreamPort);
var services = new List<Service>();
var serviceProvider = new NoServiceProvider(services);
var service = serviceProvider.Get();
var firstHostAndPort = service[0].HostAndPort;
return firstHostAndPort;
};
if (isAuthenticated)
{

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Ocelot.Values;
namespace Ocelot.ServiceDiscovery
{
public interface IServiceProvider
{
List<Service> Get();
}
}

View File

@ -0,0 +1,9 @@
using Ocelot.Configuration;
namespace Ocelot.ServiceDiscovery
{
public interface IServiceProviderFactory
{
Ocelot.ServiceDiscovery.IServiceProvider Get(ReRoute reRoute);
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using Ocelot.Values;
namespace Ocelot.ServiceDiscovery
{
public class NoServiceProvider : IServiceProvider
{
private List<Service> _services;
public NoServiceProvider(List<Service> services)
{
_services = services;
}
public List<Service> Get()
{
return _services;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using Ocelot.Configuration;
namespace Ocelot.ServiceDiscovery
{
public class ServiceProviderFactory : IServiceProviderFactory
{
public Ocelot.ServiceDiscovery.IServiceProvider Get(ReRoute reRoute)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,13 @@
namespace Ocelot.Values
{
public class Service
{
public Service(string name, HostAndPort hostAndPort)
{
Name = name;
HostAndPort = hostAndPort;
}
public string Name {get; private set;}
public HostAndPort HostAndPort {get; private set;}
}
}

View File

@ -14,7 +14,7 @@ namespace Ocelot.UnitTests
{
private HostAndPort _hostAndPort;
private Response<HostAndPort> _result;
private LeastConnection _leastConnection;
private LeastConnectionLoadBalancer _leastConnection;
private List<Service> _services;
public LeastConnectionTests()
@ -53,7 +53,7 @@ namespace Ocelot.UnitTests
};
_services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName);
_leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease();
@ -80,7 +80,7 @@ namespace Ocelot.UnitTests
};
_services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName);
_leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease();
@ -111,7 +111,7 @@ namespace Ocelot.UnitTests
};
_services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName);
_leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease();
@ -178,7 +178,7 @@ namespace Ocelot.UnitTests
private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName)
{
_services = services;
_leastConnection = new LeastConnection(() => _services, serviceName);
_leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
}
private void WhenTheLoadBalancerStarts(List<Service> services, string serviceName)
@ -203,13 +203,13 @@ namespace Ocelot.UnitTests
}
}
public class LeastConnection : ILoadBalancer
public class LeastConnectionLoadBalancer : ILoadBalancer
{
private Func<List<Service>> _services;
private List<Lease> _leases;
private string _serviceName;
public LeastConnection(Func<List<Service>> services, string serviceName)
public LeastConnectionLoadBalancer(Func<List<Service>> services, string serviceName)
{
_services = services;
_serviceName = serviceName;

View File

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Responses;
@ -14,10 +17,12 @@ namespace Ocelot.UnitTests
private ReRoute _reRoute;
private LoadBalancerFactory _factory;
private ILoadBalancer _result;
private Mock<Ocelot.ServiceDiscovery.IServiceProvider> _serviceProvider;
public LoadBalancerFactoryTests()
{
_factory = new LoadBalancerFactory();
_serviceProvider = new Mock<Ocelot.ServiceDiscovery.IServiceProvider>();
_factory = new LoadBalancerFactory(_serviceProvider.Object);
}
[Fact]
@ -36,8 +41,8 @@ namespace Ocelot.UnitTests
public void should_return_round_robin_load_balancer()
{
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("RoundRobin")
.Build();
.WithLoadBalancer("RoundRobin")
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.When(x => x.WhenIGetTheLoadBalancer())
@ -45,6 +50,38 @@ namespace Ocelot.UnitTests
.BDDfy();
}
[Fact]
public void should_return_round_least_connection_balancer()
{
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("LeastConnection")
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheLoadBalancerIsReturned<LeastConnectionLoadBalancer>())
.BDDfy();
}
[Fact]
public void should_call_service_provider()
{
var reRoute = new ReRouteBuilder()
.WithLoadBalancer("RoundRobin")
.Build();
this.Given(x => x.GivenAReRoute(reRoute))
.When(x => x.WhenIGetTheLoadBalancer())
.Then(x => x.ThenTheServiceProviderIsCalledCorrectly(reRoute))
.BDDfy();
}
private void ThenTheServiceProviderIsCalledCorrectly(ReRoute reRoute)
{
_serviceProvider
.Verify(x => x.Get(), Times.Once);
}
private void GivenAReRoute(ReRoute reRoute)
{
_reRoute = reRoute;
@ -61,16 +98,62 @@ namespace Ocelot.UnitTests
}
}
public class NoLoadBalancer : ILoadBalancer
public class NoLoadBalancerTests
{
Response<HostAndPort> ILoadBalancer.Lease()
private List<Service> _services;
private NoLoadBalancer _loadBalancer;
private Response<HostAndPort> _result;
[Fact]
public void should_return_host_and_port()
{
throw new NotImplementedException();
var hostAndPort = new HostAndPort("127.0.0.1", 80);
var services = new List<Service>
{
new Service("product", hostAndPort)
};
this.Given(x => x.GivenServices(services))
.When(x => x.WhenIGetTheNextHostAndPort())
.Then(x => x.ThenTheHostAndPortIs(hostAndPort))
.BDDfy();
}
Response ILoadBalancer.Release(HostAndPort hostAndPort)
private void GivenServices(List<Service> services)
{
throw new NotImplementedException();
_services = services;
}
private void WhenIGetTheNextHostAndPort()
{
_loadBalancer = new NoLoadBalancer(_services);
_result = _loadBalancer.Lease();
}
private void ThenTheHostAndPortIs(HostAndPort expected)
{
_result.Data.ShouldBe(expected);
}
}
public class NoLoadBalancer : ILoadBalancer
{
private List<Service> _services;
public NoLoadBalancer(List<Service> services)
{
_services = services;
}
public Response<HostAndPort> Lease()
{
var service = _services.FirstOrDefault();
return new OkResponse<HostAndPort>(service.HostAndPort);
}
public Response Release(HostAndPort hostAndPort)
{
return new OkResponse();
}
}
@ -81,14 +164,23 @@ namespace Ocelot.UnitTests
public class LoadBalancerFactory : ILoadBalancerFactory
{
private Ocelot.ServiceDiscovery.IServiceProvider _serviceProvider;
public LoadBalancerFactory(Ocelot.ServiceDiscovery.IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ILoadBalancer Get(ReRoute reRoute)
{
switch (reRoute.LoadBalancer)
{
case "RoundRobin":
return new RoundRobinLoadBalancer(null);
return new RoundRobinLoadBalancer(_serviceProvider.Get());
case "LeastConnection":
return new LeastConnectionLoadBalancer(() => _serviceProvider.Get(), reRoute.ServiceName);
default:
return new NoLoadBalancer();
return new NoLoadBalancer(_serviceProvider.Get());
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery;
using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests
{
public class NoServiceProviderTests
{
private NoServiceProvider _serviceProvider;
private HostAndPort _hostAndPort;
private List<Service> _result;
private List<Service> _expected;
[Fact]
public void should_return_services()
{
var hostAndPort = new HostAndPort("127.0.0.1", 80);
var services = new List<Service>
{
new Service("product", hostAndPort)
};
this.Given(x => x.GivenAHostAndPort(services))
.When(x => x.WhenIGetTheService())
.Then(x => x.ThenTheFollowingIsReturned(services))
.BDDfy();
}
private void GivenAHostAndPort(List<Service> services)
{
_expected = services;
}
private void WhenIGetTheService()
{
_serviceProvider = new NoServiceProvider(_expected);
_result = _serviceProvider.Get();
}
private void ThenTheFollowingIsReturned(List<Service> services)
{
_result[0].HostAndPort.DownstreamHost.ShouldBe(services[0].HostAndPort.DownstreamHost);
_result[0].HostAndPort.DownstreamPort.ShouldBe(services[0].HostAndPort.DownstreamPort);
_result[0].Name.ShouldBe(services[0].Name);
}
}
}

View File

@ -11,19 +11,19 @@ namespace Ocelot.UnitTests
public class RoundRobinTests
{
private readonly RoundRobinLoadBalancer _roundRobin;
private readonly List<HostAndPort> _hostAndPorts;
private readonly List<Service> _services;
private Response<HostAndPort> _hostAndPort;
public RoundRobinTests()
{
_hostAndPorts = new List<HostAndPort>
_services = new List<Service>
{
new HostAndPort("127.0.0.1", 5000),
new HostAndPort("127.0.0.1", 5001),
new HostAndPort("127.0.0.1", 5001)
new Service("product", new HostAndPort("127.0.0.1", 5000)),
new Service("product", new HostAndPort("127.0.0.1", 5001)),
new Service("product", new HostAndPort("127.0.0.1", 5001))
};
_roundRobin = new RoundRobinLoadBalancer(_hostAndPorts);
_roundRobin = new RoundRobinLoadBalancer(_services);
}
[Fact]
@ -46,11 +46,11 @@ namespace Ocelot.UnitTests
while (stopWatch.ElapsedMilliseconds < 1000)
{
var address = _roundRobin.Lease();
address.Data.ShouldBe(_hostAndPorts[0]);
address.Data.ShouldBe(_services[0].HostAndPort);
address = _roundRobin.Lease();
address.Data.ShouldBe(_hostAndPorts[1]);
address.Data.ShouldBe(_services[1].HostAndPort);
address = _roundRobin.Lease();
address.Data.ShouldBe(_hostAndPorts[2]);
address.Data.ShouldBe(_services[2].HostAndPort);
}
}
@ -61,7 +61,7 @@ namespace Ocelot.UnitTests
private void ThenTheNextAddressIndexIs(int index)
{
_hostAndPort.Data.ShouldBe(_hostAndPorts[index]);
_hostAndPort.Data.ShouldBe(_services[index].HostAndPort);
}
}
@ -73,24 +73,24 @@ namespace Ocelot.UnitTests
public class RoundRobinLoadBalancer : ILoadBalancer
{
private readonly List<HostAndPort> _hostAndPorts;
private readonly List<Service> _services;
private int _last;
public RoundRobinLoadBalancer(List<HostAndPort> hostAndPorts)
public RoundRobinLoadBalancer(List<Service> services)
{
_hostAndPorts = hostAndPorts;
_services = services;
}
public Response<HostAndPort> Lease()
{
if (_last >= _hostAndPorts.Count)
if (_last >= _services.Count)
{
_last = 0;
}
var next = _hostAndPorts[_last];
var next = _services[_last];
_last++;
return new OkResponse<HostAndPort>(next);
return new OkResponse<HostAndPort>(next.HostAndPort);
}
public Response Release(HostAndPort hostAndPort)

View File

@ -0,0 +1,50 @@
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.ServiceDiscovery;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests
{
public class ServiceProviderFactoryTests
{
private ReRoute _reRote;
private IServiceProvider _result;
private ServiceProviderFactory _factory;
public ServiceProviderFactoryTests()
{
_factory = new ServiceProviderFactory();
}
[Fact]
public void should_return_no_service_provider()
{
var reRoute = new ReRouteBuilder()
.WithDownstreamHost("127.0.0.1")
.WithDownstreamPort(80)
.Build();
this.Given(x => x.GivenTheReRoute(reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<NoServiceProvider>())
.BDDfy();
}
private void GivenTheReRoute(ReRoute reRoute)
{
_reRote = reRoute;
}
private void WhenIGetTheServiceProvider()
{
_result = _factory.Get(_reRote);
}
private void ThenTheServiceProviderIs<T>()
{
_result.ShouldBeOfType<T>();
}
}
}

View File

@ -86,6 +86,7 @@ namespace Ocelot.UnitTests
{
_repository = repository;
}
public void Register(Service serviceNameAndAddress)
{
_repository.Set(serviceNameAndAddress);
@ -97,17 +98,6 @@ namespace Ocelot.UnitTests
}
}
public class Service
{
public Service(string name, HostAndPort hostAndPort)
{
Name = name;
HostAndPort = hostAndPort;
}
public string Name {get; private set;}
public HostAndPort HostAndPort {get; private set;}
}
public interface IServiceRepository
{
List<Service> Get(string serviceName);