decided to stick a basic cache in for downstream route creator, can make fancy if required (#359)

This commit is contained in:
Tom Pallister 2018-05-15 20:39:15 +01:00 committed by GitHub
parent 061a90f1dd
commit a55c75efdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 2 deletions

View File

@ -1,5 +1,6 @@
namespace Ocelot.DownstreamRouteFinder.Finder namespace Ocelot.DownstreamRouteFinder.Finder
{ {
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using Configuration; using Configuration;
using Configuration.Builder; using Configuration.Builder;
@ -12,10 +13,12 @@
public class DownstreamRouteCreator : IDownstreamRouteProvider public class DownstreamRouteCreator : IDownstreamRouteProvider
{ {
private readonly IQoSOptionsCreator _qoSOptionsCreator; private readonly IQoSOptionsCreator _qoSOptionsCreator;
private readonly ConcurrentDictionary<string, OkResponse<DownstreamRoute>> _cache;
public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator) public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator)
{ {
_qoSOptionsCreator = qoSOptionsCreator; _qoSOptionsCreator = qoSOptionsCreator;
_cache = new ConcurrentDictionary<string, OkResponse<DownstreamRoute>>();
} }
public Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost) public Response<DownstreamRoute> Get(string upstreamUrlPath, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost)
@ -33,6 +36,11 @@
var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions); var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions);
if(_cache.TryGetValue(loadBalancerKey, out var downstreamRoute))
{
return downstreamRoute;
}
var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod }); var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod });
var downstreamReRoute = new DownstreamReRouteBuilder() var downstreamReRoute = new DownstreamReRouteBuilder()
@ -51,7 +59,11 @@
.WithUpstreamHttpMethod(new List<string>(){ upstreamHttpMethod }) .WithUpstreamHttpMethod(new List<string>(){ upstreamHttpMethod })
.Build(); .Build();
return new OkResponse<DownstreamRoute>(new DownstreamRoute(new List<PlaceholderNameAndValue>(), reRoute)); downstreamRoute = new OkResponse<DownstreamRoute>(new DownstreamRoute(new List<PlaceholderNameAndValue>(), reRoute));
_cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute);
return downstreamRoute;
} }
private static string RemoveQueryString(string downstreamPath) private static string RemoveQueryString(string downstreamPath)
@ -67,12 +79,23 @@
private static string GetDownstreamPath(string upstreamUrlPath) private static string GetDownstreamPath(string upstreamUrlPath)
{ {
if(upstreamUrlPath.IndexOf('/', 1) == -1)
{
return "/";
}
return upstreamUrlPath return upstreamUrlPath
.Substring(upstreamUrlPath.IndexOf('/', 1)); .Substring(upstreamUrlPath.IndexOf('/', 1));
} }
private static string GetServiceName(string upstreamUrlPath) private static string GetServiceName(string upstreamUrlPath)
{ {
if(upstreamUrlPath.IndexOf('/', 1) == -1)
{
return upstreamUrlPath
.Substring(1);
}
return upstreamUrlPath return upstreamUrlPath
.Substring(1, upstreamUrlPath.IndexOf('/', 1)) .Substring(1, upstreamUrlPath.IndexOf('/', 1))
.TrimEnd('/'); .TrimEnd('/');

View File

@ -6,6 +6,7 @@ using System.Net.Http;
namespace Ocelot.UnitTests.DownstreamRouteFinder namespace Ocelot.UnitTests.DownstreamRouteFinder
{ {
using System;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
@ -26,6 +27,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
private string _upstreamHttpMethod; private string _upstreamHttpMethod;
private IInternalConfiguration _configuration; private IInternalConfiguration _configuration;
private Mock<IQoSOptionsCreator> _qosOptionsCreator; private Mock<IQoSOptionsCreator> _qosOptionsCreator;
private Response<DownstreamRoute> _resultTwo;
public DownstreamRouteCreatorTests() public DownstreamRouteCreatorTests()
{ {
@ -50,6 +52,32 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_cache_downstream_route()
{
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreate())
.And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreateAgain())
.Then(_ => ThenTheDownstreamRoutesAreTheSameReference())
.BDDfy();
}
[Fact]
public void should_not_cache_downstream_route()
{
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/"))
.When(_ => WhenICreate())
.And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/"))
.When(_ => WhenICreateAgain())
.Then(_ => ThenTheDownstreamRoutesAreTheNotSameReference())
.BDDfy();
}
[Fact] [Fact]
public void should_create_downstream_route_with_no_path() public void should_create_downstream_route_with_no_path()
{ {
@ -62,6 +90,30 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_create_downstream_route_with_only_first_segment_no_traling_slash()
{
var upstreamUrlPath = "/auth";
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath))
.When(_ => WhenICreate())
.Then(_ => ThenTheDownstreamPathIsForwardSlash())
.BDDfy();
}
[Fact]
public void should_create_downstream_route_with_segments_no_traling_slash()
{
var upstreamUrlPath = "/auth/test";
var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath))
.When(_ => WhenICreate())
.Then(_ => ThenThePathDoesNotHaveTrailingSlash())
.BDDfy();
}
[Fact] [Fact]
public void should_create_downstream_route_and_remove_query_string() public void should_create_downstream_route_and_remove_query_string()
{ {
@ -143,6 +195,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET"); _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET");
} }
private void ThenThePathDoesNotHaveTrailingSlash()
{
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
}
private void ThenTheQueryStringIsRemoved() private void ThenTheQueryStringIsRemoved()
{ {
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");
@ -190,5 +249,20 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
{ {
_result = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost); _result = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost);
} }
private void WhenICreateAgain()
{
_resultTwo = _creator.Get(_upstreamUrlPath, _upstreamHttpMethod, _configuration, _upstreamHost);
}
private void ThenTheDownstreamRoutesAreTheSameReference()
{
_result.ShouldBe(_resultTwo);
}
private void ThenTheDownstreamRoutesAreTheNotSameReference()
{
_result.ShouldNotBe(_resultTwo);
}
} }
} }