Merge branch 'CustomLoadBalancers' of https://github.com/DavidLievrouw/Ocelot into DavidLievrouw-CustomLoadBalancers

This commit is contained in:
TomPallister
2020-04-13 11:06:49 +01:00
17 changed files with 767 additions and 67 deletions

View File

@ -3,6 +3,9 @@ using Microsoft.Extensions.DependencyInjection;
using Ocelot.Middleware.Multiplexer;
using System;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.ServiceDiscovery.Providers;
namespace Ocelot.DependencyInjection
{
@ -25,6 +28,23 @@ namespace Ocelot.DependencyInjection
IOcelotBuilder AddTransientDefinedAggregator<T>()
where T : class, IDefinedAggregator;
IOcelotBuilder AddCustomLoadBalancer<T>()
where T : ILoadBalancer, new();
IOcelotBuilder AddCustomLoadBalancer<T>(Func<T> loadBalancerFactoryFunc)
where T : ILoadBalancer;
IOcelotBuilder AddCustomLoadBalancer<T>(Func<IServiceProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer;
IOcelotBuilder AddCustomLoadBalancer<T>(
Func<DownstreamReRoute, IServiceDiscoveryProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer;
IOcelotBuilder AddCustomLoadBalancer<T>(
Func<IServiceProvider, DownstreamReRoute, IServiceDiscoveryProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer;
IOcelotBuilder AddConfigPlaceholders();
}
}

View File

@ -1,3 +1,5 @@
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Configuration.ChangeTracking;
namespace Ocelot.DependencyInjection
@ -87,6 +89,10 @@ namespace Ocelot.DependencyInjection
Services.TryAddSingleton<IFileConfigurationRepository, DiskFileConfigurationRepository>();
Services.TryAddSingleton<IFileConfigurationSetter, FileAndInternalConfigurationSetter>();
Services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
Services.AddSingleton<ILoadBalancerCreator, NoLoadBalancerCreator>();
Services.AddSingleton<ILoadBalancerCreator, RoundRobinCreator>();
Services.AddSingleton<ILoadBalancerCreator, CookieStickySessionsCreator>();
Services.AddSingleton<ILoadBalancerCreator, LeastConnectionCreator>();
Services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
Services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
Services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
@ -169,6 +175,47 @@ namespace Ocelot.DependencyInjection
return this;
}
public IOcelotBuilder AddCustomLoadBalancer<T>()
where T : ILoadBalancer, new()
{
AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) => new T());
return this;
}
public IOcelotBuilder AddCustomLoadBalancer<T>(Func<T> loadBalancerFactoryFunc)
where T : ILoadBalancer
{
AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) =>
loadBalancerFactoryFunc());
return this;
}
public IOcelotBuilder AddCustomLoadBalancer<T>(Func<IServiceProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer
{
AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) =>
loadBalancerFactoryFunc(provider));
return this;
}
public IOcelotBuilder AddCustomLoadBalancer<T>(Func<DownstreamReRoute, IServiceDiscoveryProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer
{
AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) =>
loadBalancerFactoryFunc(reRoute, serviceDiscoveryProvider));
return this;
}
public IOcelotBuilder AddCustomLoadBalancer<T>(Func<IServiceProvider, DownstreamReRoute, IServiceDiscoveryProvider, T> loadBalancerFactoryFunc)
where T : ILoadBalancer
{
Services.AddSingleton<ILoadBalancerCreator>(provider =>
new DelegateInvokingLoadBalancerCreator<T>(
(reRoute, serviceDiscoveryProvider) =>
loadBalancerFactoryFunc(provider, reRoute, serviceDiscoveryProvider)));
return this;
}
private void AddSecurity()
{
Services.TryAddSingleton<ISecurityOptionsCreator, SecurityOptionsCreator>();

View File

@ -0,0 +1,20 @@
namespace Ocelot.LoadBalancer.LoadBalancers
{
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Infrastructure;
using Ocelot.ServiceDiscovery.Providers;
public class CookieStickySessionsCreator : ILoadBalancerCreator
{
public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider)
{
var loadBalancer = new RoundRobin(async () => await serviceProvider.Get());
var bus = new InMemoryBus<StickySession>();
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key,
reRoute.LoadBalancerOptions.ExpiryInMs, bus);
}
public string Type => nameof(CookieStickySessions);
}
}

View File

@ -0,0 +1,25 @@
namespace Ocelot.LoadBalancer.LoadBalancers
{
using System;
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery.Providers;
public class DelegateInvokingLoadBalancerCreator<T> : ILoadBalancerCreator
where T : ILoadBalancer
{
private readonly Func<DownstreamReRoute, IServiceDiscoveryProvider, ILoadBalancer> _creatorFunc;
public DelegateInvokingLoadBalancerCreator(
Func<DownstreamReRoute, IServiceDiscoveryProvider, ILoadBalancer> creatorFunc)
{
_creatorFunc = creatorFunc;
}
public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider)
{
return _creatorFunc(reRoute, serviceProvider);
}
public string Type => typeof(T).Name;
}
}

View File

@ -0,0 +1,11 @@
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery.Providers;
namespace Ocelot.LoadBalancer.LoadBalancers
{
public interface ILoadBalancerCreator
{
ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider);
string Type { get; }
}
}

View File

@ -0,0 +1,15 @@
namespace Ocelot.LoadBalancer.LoadBalancers
{
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery.Providers;
public class LeastConnectionCreator : ILoadBalancerCreator
{
public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider)
{
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
}
public string Type => nameof(LeastConnection);
}
}

View File

@ -1,47 +1,42 @@
using Ocelot.Configuration;
using Ocelot.Infrastructure;
using Ocelot.Responses;
using Ocelot.ServiceDiscovery;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.LoadBalancers
namespace Ocelot.LoadBalancer.LoadBalancers
{
using System.Collections.Generic;
using System.Linq;
using Ocelot.Configuration;
using Ocelot.Responses;
using System.Threading.Tasks;
using Ocelot.ServiceDiscovery;
public class LoadBalancerFactory : ILoadBalancerFactory
{
private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory;
private readonly IEnumerable<ILoadBalancerCreator> _loadBalancerCreators;
public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory)
public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory, IEnumerable<ILoadBalancerCreator> loadBalancerCreators)
{
_serviceProviderFactory = serviceProviderFactory;
_loadBalancerCreators = loadBalancerCreators;
}
public async Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
public Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
{
var response = _serviceProviderFactory.Get(config, reRoute);
var serviceProviderFactoryResponse = _serviceProviderFactory.Get(config, reRoute);
if (response.IsError)
Response<ILoadBalancer> response;
if (serviceProviderFactoryResponse.IsError)
{
return new ErrorResponse<ILoadBalancer>(response.Errors);
response = new ErrorResponse<ILoadBalancer>(serviceProviderFactoryResponse.Errors);
}
else
{
var serviceProvider = serviceProviderFactoryResponse.Data;
var requestedType = reRoute.LoadBalancerOptions?.Type ?? nameof(NoLoadBalancer);
var applicableCreator = _loadBalancerCreators.Single(c => c.Type == requestedType);
var createdLoadBalancer = applicableCreator.Create(reRoute, serviceProvider);
response = new OkResponse<ILoadBalancer>(createdLoadBalancer);
}
var serviceProvider = response.Data;
switch (reRoute.LoadBalancerOptions?.Type)
{
case nameof(RoundRobin):
return new OkResponse<ILoadBalancer>(new RoundRobin(async () => await serviceProvider.Get()));
case nameof(LeastConnection):
return new OkResponse<ILoadBalancer>(new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName));
case nameof(CookieStickySessions):
var loadBalancer = new RoundRobin(async () => await serviceProvider.Get());
var bus = new InMemoryBus<StickySession>();
return new OkResponse<ILoadBalancer>(new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus));
default:
return new OkResponse<ILoadBalancer>(new NoLoadBalancer(async () => await serviceProvider.Get()));
}
return Task.FromResult(response);
}
}
}

View File

@ -0,0 +1,15 @@
namespace Ocelot.LoadBalancer.LoadBalancers
{
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery.Providers;
public class NoLoadBalancerCreator : ILoadBalancerCreator
{
public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider)
{
return new NoLoadBalancer(async () => await serviceProvider.Get());
}
public string Type => nameof(NoLoadBalancer);
}
}

View File

@ -1,12 +1,12 @@
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ocelot.LoadBalancer.LoadBalancers
namespace Ocelot.LoadBalancer.LoadBalancers
{
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class RoundRobin : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _services;

View File

@ -0,0 +1,15 @@
namespace Ocelot.LoadBalancer.LoadBalancers
{
using Ocelot.Configuration;
using Ocelot.ServiceDiscovery.Providers;
public class RoundRobinCreator : ILoadBalancerCreator
{
public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider)
{
return new RoundRobin(async () => await serviceProvider.Get());
}
public string Type => nameof(RoundRobin);
}
}