mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-20 07:22:50 +08:00
Checkin for caching the template matching for significant route finder performance improvements (#728)
This commit is contained in:
parent
ac211886f1
commit
9bbb6364f2
@ -28,7 +28,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
||||
|
||||
foreach (var reRoute in applicableReRoutes)
|
||||
{
|
||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern.Template, reRoute.UpstreamTemplatePattern.ContainsQueryString);
|
||||
var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern);
|
||||
|
||||
if (urlMatch.Data.Match)
|
||||
{
|
||||
|
@ -1,9 +1,10 @@
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public interface IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString);
|
||||
}
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public interface IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, UpstreamPathTemplate pathTemplate);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,23 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class RegExUrlMatcher : IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, string upstreamUrlPathTemplate, bool containsQueryString)
|
||||
{
|
||||
var regex = new Regex(upstreamUrlPathTemplate);
|
||||
|
||||
if (!containsQueryString)
|
||||
{
|
||||
return regex.IsMatch(upstreamUrlPath)
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
|
||||
return regex.IsMatch($"{upstreamUrlPath}{upstreamQueryString}")
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Text.RegularExpressions;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.DownstreamRouteFinder.UrlMatcher
|
||||
{
|
||||
public class RegExUrlMatcher : IUrlPathToUrlTemplateMatcher
|
||||
{
|
||||
public Response<UrlMatch> Match(string upstreamUrlPath, string upstreamQueryString, UpstreamPathTemplate pathTemplate)
|
||||
{
|
||||
if (!pathTemplate.ContainsQueryString)
|
||||
{
|
||||
return pathTemplate.Pattern.IsMatch(upstreamUrlPath)
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
|
||||
return pathTemplate.Pattern.IsMatch($"{upstreamUrlPath}{upstreamQueryString}")
|
||||
? new OkResponse<UrlMatch>(new UrlMatch(true))
|
||||
: new OkResponse<UrlMatch>(new UrlMatch(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,28 @@
|
||||
namespace Ocelot.Values
|
||||
{
|
||||
public class UpstreamPathTemplate
|
||||
{
|
||||
public UpstreamPathTemplate(string template, int priority, bool containsQueryString, string originalValue)
|
||||
{
|
||||
Template = template;
|
||||
Priority = priority;
|
||||
ContainsQueryString = containsQueryString;
|
||||
OriginalValue = originalValue;
|
||||
}
|
||||
|
||||
public string Template { get; }
|
||||
|
||||
public int Priority { get; }
|
||||
|
||||
public bool ContainsQueryString { get; }
|
||||
|
||||
public string OriginalValue { get; }
|
||||
}
|
||||
}
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ocelot.Values
|
||||
{
|
||||
public class UpstreamPathTemplate
|
||||
{
|
||||
public UpstreamPathTemplate(string template, int priority, bool containsQueryString, string originalValue)
|
||||
{
|
||||
Template = template;
|
||||
Priority = priority;
|
||||
ContainsQueryString = containsQueryString;
|
||||
OriginalValue = originalValue;
|
||||
Pattern = template == null ?
|
||||
new Regex("$^", RegexOptions.Compiled | RegexOptions.Singleline) :
|
||||
new Regex(template, RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
}
|
||||
|
||||
public string Template { get; }
|
||||
|
||||
public int Priority { get; }
|
||||
|
||||
public bool ContainsQueryString { get; }
|
||||
|
||||
public string OriginalValue { get; }
|
||||
|
||||
public Regex Pattern { get; }
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,52 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Validators;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
|
||||
namespace Ocelot.Benchmarks
|
||||
{
|
||||
[Config(typeof(UrlPathToUrlPathTemplateMatcherBenchmarks))]
|
||||
public class UrlPathToUrlPathTemplateMatcherBenchmarks : ManualConfig
|
||||
{
|
||||
private RegExUrlMatcher _urlPathMatcher;
|
||||
private string _downstreamUrlPath;
|
||||
private string _downstreamUrlPathTemplate;
|
||||
private string _upstreamQuery;
|
||||
|
||||
public UrlPathToUrlPathTemplateMatcherBenchmarks()
|
||||
{
|
||||
Add(StatisticColumn.AllStatistics);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(BaselineValidator.FailOnError);
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void SetUp()
|
||||
{
|
||||
_urlPathMatcher = new RegExUrlMatcher();
|
||||
_downstreamUrlPath = "api/product/products/1/variants/?soldout=false";
|
||||
_downstreamUrlPathTemplate = "api/product/products/{productId}/variants/";
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void Baseline()
|
||||
{
|
||||
_urlPathMatcher.Match(_downstreamUrlPath, _upstreamQuery, _downstreamUrlPathTemplate, false);
|
||||
}
|
||||
|
||||
// * Summary *
|
||||
|
||||
// BenchmarkDotNet=v0.10.13, OS=macOS 10.12.6 (16G1212) [Darwin 16.7.0]
|
||||
// Intel Core i5-4278U CPU 2.60GHz (Haswell), 1 CPU, 4 logical cores and 2 physical cores
|
||||
// .NET Core SDK=2.1.4
|
||||
// [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
|
||||
// DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
|
||||
// Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s |
|
||||
// ----------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|----------:|
|
||||
// Benchmark1 | 3.133 us | 0.0492 us | 0.0460 us | 0.0119 us | 3.082 us | 3.100 us | 3.122 us | 3.168 us | 3.233 us | 319,161.9 |
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Validators;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.Benchmarks
|
||||
{
|
||||
[Config(typeof(UrlPathToUrlPathTemplateMatcherBenchmarks))]
|
||||
public class UrlPathToUrlPathTemplateMatcherBenchmarks : ManualConfig
|
||||
{
|
||||
private RegExUrlMatcher _urlPathMatcher;
|
||||
private UpstreamPathTemplate _pathTemplate;
|
||||
private string _downstreamUrlPath;
|
||||
private string _upstreamQuery;
|
||||
|
||||
public UrlPathToUrlPathTemplateMatcherBenchmarks()
|
||||
{
|
||||
Add(StatisticColumn.AllStatistics);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(BaselineValidator.FailOnError);
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void SetUp()
|
||||
{
|
||||
_urlPathMatcher = new RegExUrlMatcher();
|
||||
_pathTemplate = new UpstreamPathTemplate("api/product/products/{productId}/variants/", 0, false, null);
|
||||
_downstreamUrlPath = "api/product/products/1/variants/?soldout=false";
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void Baseline()
|
||||
{
|
||||
_urlPathMatcher.Match(_downstreamUrlPath, _upstreamQuery, _pathTemplate);
|
||||
}
|
||||
|
||||
// * Summary *
|
||||
|
||||
// BenchmarkDotNet=v0.10.13, OS=macOS 10.12.6 (16G1212) [Darwin 16.7.0]
|
||||
// Intel Core i5-4278U CPU 2.60GHz (Haswell), 1 CPU, 4 logical cores and 2 physical cores
|
||||
// .NET Core SDK=2.1.4
|
||||
// [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
|
||||
// DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
|
||||
// Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s |
|
||||
// ----------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|----------:|
|
||||
// Benchmark1 | 3.133 us | 0.0492 us | 0.0460 us | 0.0119 us | 3.082 us | 3.100 us | 3.122 us | 3.168 us | 3.233 us | 319,161.9 |
|
||||
}
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||
.When(x => x.WhenICallTheFinder())
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
@ -677,7 +677,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test"))
|
||||
.Build()
|
||||
)))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0))
|
||||
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 1))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
@ -706,32 +707,32 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly()
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.Template, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once);
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly(int times)
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly(int times, int index = 0)
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Exactly(times));
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[index].UpstreamTemplatePattern), Times.Exactly(times));
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Once);
|
||||
.Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheUrlMatcherIsNotCalled()
|
||||
{
|
||||
_mockMatcher
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern.OriginalValue, _reRoutesConfig[0].UpstreamTemplatePattern.ContainsQueryString), Times.Never);
|
||||
.Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Never);
|
||||
}
|
||||
|
||||
private void GivenTheUrlMatcherReturns(Response<UrlMatch> match)
|
||||
{
|
||||
_match = match;
|
||||
_mockMatcher
|
||||
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<UpstreamPathTemplate>()))
|
||||
.Returns(_match);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
@ -270,7 +271,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
|
||||
|
||||
private void WhenIMatchThePaths()
|
||||
{
|
||||
_result = _urlMatcher.Match(_path, _queryString, _downstreamPathTemplate, _containsQueryString);
|
||||
_result = _urlMatcher.Match(_path, _queryString, new UpstreamPathTemplate(_downstreamPathTemplate, 0, _containsQueryString, _downstreamPathTemplate));
|
||||
}
|
||||
|
||||
private void ThenTheResultIsTrue()
|
||||
|
Loading…
x
Reference in New Issue
Block a user