From f285b0e0add544a14e443144477cf3d389a3e229 Mon Sep 17 00:00:00 2001 From: Tom Gardham-Pallister Date: Fri, 3 Feb 2017 08:00:07 +0000 Subject: [PATCH] plugged load balancer middleware into Ocelot pipeline, load balanced downstream host and port now used by url creator middleware --- .../Configuration/Builder/ReRouteBuilder.cs | 9 +++++- .../Creator/FileOcelotConfigurationCreator.cs | 10 ++++--- src/Ocelot/Configuration/ReRoute.cs | 4 ++- .../DownstreamUrlCreatorMiddleware.cs | 7 +---- .../Middleware/LoadBalancingMiddleware.cs | 28 +++++++++++-------- .../LoadBalancingMiddlewareExtensions.cs | 2 +- .../Middleware/OcelotMiddlewareExtensions.cs | 4 +++ .../DownstreamUrlCreatorMiddlewareTests.cs | 14 +++++++++- .../LoadBalancerMiddlewareTests.cs | 2 +- 9 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 9579330c..e12b1e4b 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -6,6 +6,7 @@ namespace Ocelot.Configuration.Builder { public class ReRouteBuilder { + private string _loadBalancerKey; private string _downstreamPathTemplate; private string _upstreamTemplate; private string _upstreamTemplatePattern; @@ -199,6 +200,12 @@ namespace Ocelot.Configuration.Builder return this; } + public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey) + { + _loadBalancerKey = loadBalancerKey; + return this; + } + public ReRoute Build() { return new ReRoute(new DownstreamPathTemplate(_downstreamPathTemplate), _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, @@ -206,7 +213,7 @@ namespace Ocelot.Configuration.Builder _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey, _isCached, _fileCacheOptions, _serviceName, _useServiceDiscovery, _serviceDiscoveryAddress, _serviceDiscoveryProvider, _downstreamScheme, _loadBalancer, - _downstreamHost, _dsPort); + _downstreamHost, _dsPort, _loadBalancerKey); } } } diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 61c61fae..e9ceb41a 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -104,6 +104,9 @@ namespace Ocelot.Configuration.Creator && !string.IsNullOrEmpty(globalConfiguration?.ServiceDiscoveryProvider?.Address) && !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; if (isAuthenticated) @@ -124,7 +127,7 @@ namespace Ocelot.Configuration.Creator requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, 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, @@ -134,11 +137,10 @@ namespace Ocelot.Configuration.Creator requestIdKey, isCached, new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds), fileReRoute.ServiceName, useServiceDiscovery, globalConfiguration?.ServiceDiscoveryProvider?.Provider, globalConfiguration?.ServiceDiscoveryProvider?.Address, fileReRoute.DownstreamScheme, - fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort); + fileReRoute.LoadBalancer, fileReRoute.DownstreamHost, fileReRoute.DownstreamPort,loadBalancerKey); 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($"{fileReRoute.UpstreamTemplate}{fileReRoute.UpstreamHttpMethod}", loadBalancer); + _loadBalancerHouse.Add(reRoute.LoadBalancerKey, loadBalancer); return reRoute; } diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 5cbaaebb..987f5be7 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -11,8 +11,9 @@ namespace Ocelot.Configuration List claimsToClaims, Dictionary routeClaimsRequirement, bool isAuthorised, List claimsToQueries, string requestIdKey, bool isCached, CacheOptions fileCacheOptions, string serviceName, bool useServiceDiscovery, 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; DownstreamHost = downstreamHost; DownstreamPort = downstreamPort; @@ -39,6 +40,7 @@ namespace Ocelot.Configuration ServiceDiscoveryAddress = serviceDiscoveryAddress; DownstreamScheme = downstreamScheme; } + public string LoadBalancerKey {get;private set;} public DownstreamPathTemplate DownstreamPathTemplate { get; private set; } public string UpstreamTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index bca20466..80365074 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -47,15 +47,12 @@ namespace Ocelot.DownstreamUrlCreator.Middleware var dsScheme = DownstreamRoute.ReRoute.DownstreamScheme; - //todo - get this out of scoped data repo? - var dsHostAndPort = new HostAndPort(DownstreamRoute.ReRoute.DownstreamHost, - DownstreamRoute.ReRoute.DownstreamPort); + var dsHostAndPort = HostAndPort; var dsUrl = _urlBuilder.Build(dsPath.Data.Value, dsScheme, dsHostAndPort); if (dsUrl.IsError) { - //todo - release the lb connection? _logger.LogDebug("IUrlBuilder returned an error, setting pipeline error"); SetPipelineError(dsUrl.Errors); @@ -70,8 +67,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware await _next.Invoke(context); - //todo - release the lb connection? - _logger.LogDebug("succesfully called next middleware"); } } diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index 0ef74324..99bf5167 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -31,26 +31,32 @@ namespace Ocelot.LoadBalancer.Middleware { _logger.LogDebug("started calling load balancing middleware"); - var loadBalancer = _loadBalancerHouse.Get($"{DownstreamRoute.ReRoute.UpstreamTemplate}{DownstreamRoute.ReRoute.UpstreamHttpMethod}"); - //todo check reponse and return error - - var response = loadBalancer.Data.Lease(); - //todo check reponse and return error - - SetHostAndPortForThisRequest(response.Data); + var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.LoadBalancerKey); + if(loadBalancer.IsError) + { + //set errors and return + } + + var hostAndPort = loadBalancer.Data.Lease(); + if(hostAndPort.IsError) + { + //set errors and return + } + + SetHostAndPortForThisRequest(hostAndPort.Data); + _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 { await _next.Invoke(context); - loadBalancer.Data.Release(response.Data); + loadBalancer.Data.Release(hostAndPort.Data); } 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; } diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs index 52d47bdd..0d0224b8 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddlewareExtensions.cs @@ -4,7 +4,7 @@ namespace Ocelot.LoadBalancer.Middleware { public static class LoadBalancingMiddlewareExtensions { - public static IApplicationBuilder UseLoadBalancingMiddlewareExtensions(this IApplicationBuilder builder) + public static IApplicationBuilder UseLoadBalancingMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware(); } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index dfa3b3f4..b0cd3f7e 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -18,6 +18,7 @@ namespace Ocelot.Middleware using System.Threading.Tasks; using Authorisation.Middleware; using Microsoft.AspNetCore.Http; + using Ocelot.LoadBalancer.Middleware; public static class OcelotMiddlewareExtensions { @@ -98,6 +99,9 @@ namespace Ocelot.Middleware // Now we can run any query string transformation logic 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 builder.UseDownstreamUrlCreatorMiddleware(); diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 5581a32e..a01677b2 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -36,6 +36,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator private HttpResponseMessage _result; private OkResponse _downstreamPath; private OkResponse _downstreamUrl; + private HostAndPort _hostAndPort; public DownstreamUrlCreatorMiddlewareTests() { @@ -69,14 +70,25 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator [Fact] public void should_call_dependencies_correctly() { + var hostAndPort = new HostAndPort("127.0.0.1", 80); + this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build()))) + .And(x => x.GivenTheHostAndPortIs(hostAndPort)) .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()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .BDDfy(); } + private void GivenTheHostAndPortIs(HostAndPort hostAndPort) + { + _hostAndPort = hostAndPort; + _scopedRepository + .Setup(x => x.Get("HostAndPort")) + .Returns(new OkResponse(_hostAndPort)); + } + private void TheUrlBuilderReturns(string dsUrl) { _downstreamUrl = new OkResponse(new DownstreamUrl(dsUrl)); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 7f34c903..93c63884 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -54,7 +54,7 @@ namespace Ocelot.UnitTests.LoadBalancer .UseUrls(_url) .Configure(app => { - app.UseLoadBalancingMiddlewareExtensions(); + app.UseLoadBalancingMiddleware(); }); _server = new TestServer(builder);