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.Parser;
using Ocelot.Configuration.Validator; using Ocelot.Configuration.Validator;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.ServiceDiscovery;
using Ocelot.Utilities; using Ocelot.Utilities;
using Ocelot.Values; 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 //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) 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 HostAndPort _hostAndPort;
private Response<HostAndPort> _result; private Response<HostAndPort> _result;
private LeastConnection _leastConnection; private LeastConnectionLoadBalancer _leastConnection;
private List<Service> _services; private List<Service> _services;
public LeastConnectionTests() public LeastConnectionTests()
@ -53,7 +53,7 @@ namespace Ocelot.UnitTests
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName); _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease(); var response = _leastConnection.Lease();
@ -80,7 +80,7 @@ namespace Ocelot.UnitTests
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName); _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease(); var response = _leastConnection.Lease();
@ -111,7 +111,7 @@ namespace Ocelot.UnitTests
}; };
_services = availableServices; _services = availableServices;
_leastConnection = new LeastConnection(() => _services, serviceName); _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
var response = _leastConnection.Lease(); var response = _leastConnection.Lease();
@ -178,7 +178,7 @@ namespace Ocelot.UnitTests
private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName) private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName)
{ {
_services = services; _services = services;
_leastConnection = new LeastConnection(() => _services, serviceName); _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName);
} }
private void WhenTheLoadBalancerStarts(List<Service> services, string 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 Func<List<Service>> _services;
private List<Lease> _leases; private List<Lease> _leases;
private string _serviceName; private string _serviceName;
public LeastConnection(Func<List<Service>> services, string serviceName) public LeastConnectionLoadBalancer(Func<List<Service>> services, string serviceName)
{ {
_services = services; _services = services;
_serviceName = serviceName; _serviceName = serviceName;

View File

@ -1,4 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Responses; using Ocelot.Responses;
@ -14,10 +17,12 @@ namespace Ocelot.UnitTests
private ReRoute _reRoute; private ReRoute _reRoute;
private LoadBalancerFactory _factory; private LoadBalancerFactory _factory;
private ILoadBalancer _result; private ILoadBalancer _result;
private Mock<Ocelot.ServiceDiscovery.IServiceProvider> _serviceProvider;
public LoadBalancerFactoryTests() public LoadBalancerFactoryTests()
{ {
_factory = new LoadBalancerFactory(); _serviceProvider = new Mock<Ocelot.ServiceDiscovery.IServiceProvider>();
_factory = new LoadBalancerFactory(_serviceProvider.Object);
} }
[Fact] [Fact]
@ -36,8 +41,8 @@ namespace Ocelot.UnitTests
public void should_return_round_robin_load_balancer() public void should_return_round_robin_load_balancer()
{ {
var reRoute = new ReRouteBuilder() var reRoute = new ReRouteBuilder()
.WithLoadBalancer("RoundRobin") .WithLoadBalancer("RoundRobin")
.Build(); .Build();
this.Given(x => x.GivenAReRoute(reRoute)) this.Given(x => x.GivenAReRoute(reRoute))
.When(x => x.WhenIGetTheLoadBalancer()) .When(x => x.WhenIGetTheLoadBalancer())
@ -45,6 +50,38 @@ namespace Ocelot.UnitTests
.BDDfy(); .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) private void GivenAReRoute(ReRoute reRoute)
{ {
_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 public class LoadBalancerFactory : ILoadBalancerFactory
{ {
private Ocelot.ServiceDiscovery.IServiceProvider _serviceProvider;
public LoadBalancerFactory(Ocelot.ServiceDiscovery.IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ILoadBalancer Get(ReRoute reRoute) public ILoadBalancer Get(ReRoute reRoute)
{ {
switch (reRoute.LoadBalancer) switch (reRoute.LoadBalancer)
{ {
case "RoundRobin": case "RoundRobin":
return new RoundRobinLoadBalancer(null); return new RoundRobinLoadBalancer(_serviceProvider.Get());
case "LeastConnection":
return new LeastConnectionLoadBalancer(() => _serviceProvider.Get(), reRoute.ServiceName);
default: 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 public class RoundRobinTests
{ {
private readonly RoundRobinLoadBalancer _roundRobin; private readonly RoundRobinLoadBalancer _roundRobin;
private readonly List<HostAndPort> _hostAndPorts; private readonly List<Service> _services;
private Response<HostAndPort> _hostAndPort; private Response<HostAndPort> _hostAndPort;
public RoundRobinTests() public RoundRobinTests()
{ {
_hostAndPorts = new List<HostAndPort> _services = new List<Service>
{ {
new HostAndPort("127.0.0.1", 5000), new Service("product", new HostAndPort("127.0.0.1", 5000)),
new HostAndPort("127.0.0.1", 5001), new Service("product", new HostAndPort("127.0.0.1", 5001)),
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] [Fact]
@ -46,11 +46,11 @@ namespace Ocelot.UnitTests
while (stopWatch.ElapsedMilliseconds < 1000) while (stopWatch.ElapsedMilliseconds < 1000)
{ {
var address = _roundRobin.Lease(); var address = _roundRobin.Lease();
address.Data.ShouldBe(_hostAndPorts[0]); address.Data.ShouldBe(_services[0].HostAndPort);
address = _roundRobin.Lease(); address = _roundRobin.Lease();
address.Data.ShouldBe(_hostAndPorts[1]); address.Data.ShouldBe(_services[1].HostAndPort);
address = _roundRobin.Lease(); 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) 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 public class RoundRobinLoadBalancer : ILoadBalancer
{ {
private readonly List<HostAndPort> _hostAndPorts; private readonly List<Service> _services;
private int _last; private int _last;
public RoundRobinLoadBalancer(List<HostAndPort> hostAndPorts) public RoundRobinLoadBalancer(List<Service> services)
{ {
_hostAndPorts = hostAndPorts; _services = services;
} }
public Response<HostAndPort> Lease() public Response<HostAndPort> Lease()
{ {
if (_last >= _hostAndPorts.Count) if (_last >= _services.Count)
{ {
_last = 0; _last = 0;
} }
var next = _hostAndPorts[_last]; var next = _services[_last];
_last++; _last++;
return new OkResponse<HostAndPort>(next); return new OkResponse<HostAndPort>(next.HostAndPort);
} }
public Response Release(HostAndPort 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; _repository = repository;
} }
public void Register(Service serviceNameAndAddress) public void Register(Service serviceNameAndAddress)
{ {
_repository.Set(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 public interface IServiceRepository
{ {
List<Service> Get(string serviceName); List<Service> Get(string serviceName);