plugged load balancer middleware into Ocelot pipeline, load balanced downstream host and port now used by url creator middleware

This commit is contained in:
Tom Gardham-Pallister 2017-02-03 08:00:07 +00:00
parent aef6507da3
commit f285b0e0ad
9 changed files with 54 additions and 26 deletions

View File

@ -6,6 +6,7 @@ namespace Ocelot.Configuration.Builder
{ {
public class ReRouteBuilder public class ReRouteBuilder
{ {
private string _loadBalancerKey;
private string _downstreamPathTemplate; private string _downstreamPathTemplate;
private string _upstreamTemplate; private string _upstreamTemplate;
private string _upstreamTemplatePattern; private string _upstreamTemplatePattern;
@ -199,6 +200,12 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
{
_loadBalancerKey = loadBalancerKey;
return this;
}
public ReRoute Build() public ReRoute Build()
{ {
return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern,
@ -206,7 +213,7 @@ namespace Ocelot.Configuration.Builder
_requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement,
_isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName, _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName,
_useServiceDiscovery, _serviceDiscoveryAddress, _serviceDiscoveryProvider, _downstreamScheme, _loadBalancer, _useServiceDiscovery, _serviceDiscoveryAddress, _serviceDiscoveryProvider, _downstreamScheme, _loadBalancer,
_downstreamHost, _dsPort); _downstreamHost, _dsPort, _loadBalancerKey);
} }
} }
} }

View File

@ -104,6 +104,9 @@ namespace Ocelot.Configuration.Creator
&& !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Address) && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Address)
&& !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider); && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Provider);
//note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain
var loadBalancerKey = $"{fileReRoute.UpstreamTemplate}{fileReRoute.UpstreamHttpMethod}";
ReRoute reRoute; ReRoute reRoute;
if (isAuthenticated) if (isAuthenticated)
@ -124,7 +127,7 @@ namespace Ocelot.Configuration.Creator
requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds),
fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider,
globalConfiguration?.ServiceDiscoveryProvider?.Address, fileReRoute.DownstreamScheme, globalConfiguration?.ServiceDiscoveryProvider?.Address, fileReRoute.DownstreamScheme,
fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort); fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort,loadBalancerKey);
} }
reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate), fileReRoute.UpstreamTemplate, reRoute = new ReRoute(new DownstreamPathTemplate(fileReRoute.DownstreamPathTemplate), fileReRoute.UpstreamTemplate,
@ -134,11 +137,10 @@ namespace Ocelot.Configuration.Creator
requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds),
fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider,
globalConfiguration?.ServiceDiscoveryProvider?.Address, fileReRoute.DownstreamScheme, globalConfiguration?.ServiceDiscoveryProvider?.Address, fileReRoute.DownstreamScheme,
fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort); fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort,loadBalancerKey);
var loadBalancer = _loadBalanceFactory.Get(reRoute); var loadBalancer = _loadBalanceFactory.Get(reRoute);
//todo - not sure if this is the correct key, but this is probably the only unique key i can think of _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer);
_loadBalancerHouse.Add($"{fileReRoute.UpstreamTemplate}{fileReRoute.UpstreamHttpMethod}", loadBalancer);
return reRoute; return reRoute;
} }

View File

@ -11,8 +11,9 @@ namespace Ocelot.Configuration
List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries, List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries,
string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery, string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery,
string serviceDiscoveryProvider, string serviceDiscoveryAddress, string serviceDiscoveryProvider, string serviceDiscoveryAddress,
string downstreamScheme, string loadBalancer, string downstreamHost, int downstreamPort) string downstreamScheme, string loadBalancer, string downstreamHost, int downstreamPort, string loadBalancerKey)
{ {
LoadBalancerKey = loadBalancerKey;
LoadBalancer = loadBalancer; LoadBalancer = loadBalancer;
DownstreamHost = downstreamHost; DownstreamHost = downstreamHost;
DownstreamPort = downstreamPort; DownstreamPort = downstreamPort;
@ -39,6 +40,7 @@ namespace Ocelot.Configuration
ServiceDiscoveryAddress = serviceDiscoveryAddress; ServiceDiscoveryAddress = serviceDiscoveryAddress;
DownstreamScheme = downstreamScheme; DownstreamScheme = downstreamScheme;
} }
public string LoadBalancerKey {get;private set;}
public DownstreamPathTemplate DownstreamPathTemplate { get; private set; } public DownstreamPathTemplate DownstreamPathTemplate { get; private set; }
public string UpstreamTemplate { get; private set; } public string UpstreamTemplate { get; private set; }
public string UpstreamTemplatePattern { get; private set; } public string UpstreamTemplatePattern { get; private set; }

View File

@ -47,15 +47,12 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme;
//todo - get this out of scoped data repo? var dsHostAndPort = HostAndPort;
var dsHostAndPort = new HostAndPort(DownstreamRoute.ReRoute.DownstreamHost,
DownstreamRoute.ReRoute.DownstreamPort);
var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort);
if (dsUrl.IsError) if (dsUrl.IsError)
{ {
//todo - release the lb connection?
_logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error");
SetPipelineError(dsUrl.Errors); SetPipelineError(dsUrl.Errors);
@ -70,8 +67,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
await _next.Invoke(context); await _next.Invoke(context);
//todo - release the lb connection?
_logger.LogDebug("succesfully called next middleware"); _logger.LogDebug("succesfully called next middleware");
} }
} }

View File

@ -31,26 +31,32 @@ namespace Ocelot.LoadBalancer.Middleware
{ {
_logger.LogDebug("started calling load balancing middleware"); _logger.LogDebug("started calling load balancing middleware");
var loadBalancer = _loadBalancerHouse.Get($"{DownstreamRoute.ReRoute.UpstreamTemplate}{DownstreamRoute.ReRoute.UpstreamHttpMethod}"); var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.LoadBalancerKey);
//todo check reponse and return error if(loadBalancer.IsError)
{
//set errors and return
}
var response = loadBalancer.Data.Lease(); var hostAndPort = loadBalancer.Data.Lease();
//todo check reponse and return error if(hostAndPort.IsError)
{
//set errors and return
}
SetHostAndPortForThisRequest(hostAndPort.Data);
SetHostAndPortForThisRequest(response.Data);
_logger.LogDebug("calling next middleware"); _logger.LogDebug("calling next middleware");
//todo - try next middleware if we get an exception make sure we release
//the host and port? Not sure if this is the way to go but we shall see!
try try
{ {
await _next.Invoke(context); await _next.Invoke(context);
loadBalancer.Data.Release(response.Data); loadBalancer.Data.Release(hostAndPort.Data);
} }
catch (Exception) catch (Exception)
{ {
loadBalancer.Data.Release(response.Data); loadBalancer.Data.Release(hostAndPort.Data);
_logger.LogDebug("error calling next middleware, exception will be thrown to global handler");
throw; throw;
} }

View File

@ -4,7 +4,7 @@ namespace Ocelot.LoadBalancer.Middleware
{ {
public static class LoadBalancingMiddlewareExtensions public static class LoadBalancingMiddlewareExtensions
{ {
public static IApplicationBuilder UseLoadBalancingMiddlewareExtensions(this IApplicationBuilder builder) public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder)
{ {
return builder.UseMiddleware<LoadBalancingMiddleware>(); return builder.UseMiddleware<LoadBalancingMiddleware>();
} }

View File

@ -18,6 +18,7 @@ namespace Ocelot.Middleware
using System.Threading.Tasks; using System.Threading.Tasks;
using Authorisation.Middleware; using Authorisation.Middleware;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.LoadBalancer.Middleware;
public static class OcelotMiddlewareExtensions public static class OcelotMiddlewareExtensions
{ {
@ -98,6 +99,9 @@ namespace Ocelot.Middleware
// Now we can run any query string transformation logic // Now we can run any query string transformation logic
builder.UseQueryStringBuilderMiddleware(); builder.UseQueryStringBuilderMiddleware();
// Get the load balancer for this request
builder.UseLoadBalancingMiddleware();
// This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used // This takes the downstream route we retrieved earlier and replaces any placeholders with the variables that should be used
builder.UseDownstreamUrlCreatorMiddleware(); builder.UseDownstreamUrlCreatorMiddleware();

View File

@ -36,6 +36,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
private HttpResponseMessage _result; private HttpResponseMessage _result;
private OkResponse<DownstreamPath> _downstreamPath; private OkResponse<DownstreamPath> _downstreamPath;
private OkResponse<DownstreamUrl> _downstreamUrl; private OkResponse<DownstreamUrl> _downstreamUrl;
private HostAndPort _hostAndPort;
public DownstreamUrlCreatorMiddlewareTests() public DownstreamUrlCreatorMiddlewareTests()
{ {
@ -69,14 +70,25 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
[Fact] [Fact]
public void should_call_dependencies_correctly() public void should_call_dependencies_correctly()
{ {
var hostAndPort = new HostAndPort("127.0.0.1", 80);
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build()))) this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build())))
.And(x => x.GivenTheHostAndPortIs(hostAndPort))
.And(x => x.TheUrlReplacerReturns("/api/products/1")) .And(x => x.TheUrlReplacerReturns("/api/products/1"))
.And(x => x.TheUrlBuilderReturns("http://www.bbc.co.uk/api/products/1")) .And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1"))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
private void GivenTheHostAndPortIs(HostAndPort hostAndPort)
{
_hostAndPort = hostAndPort;
_scopedRepository
.Setup(x => x.Get<HostAndPort>("HostAndPort"))
.Returns(new OkResponse<HostAndPort>(_hostAndPort));
}
private void TheUrlBuilderReturns(string dsUrl) private void TheUrlBuilderReturns(string dsUrl)
{ {
_downstreamUrl = new OkResponse<DownstreamUrl>(new DownstreamUrl(dsUrl)); _downstreamUrl = new OkResponse<DownstreamUrl>(new DownstreamUrl(dsUrl));

View File

@ -54,7 +54,7 @@ namespace Ocelot.UnitTests.LoadBalancer
.UseUrls(_url) .UseUrls(_url)
.Configure(app => .Configure(app =>
{ {
app.UseLoadBalancingMiddlewareExtensions(); app.UseLoadBalancingMiddleware();
}); });
_server = new TestServer(builder); _server = new TestServer(builder);