Skip to content

Commit

Permalink
Feature/automatic routes with sd (#351)
Browse files Browse the repository at this point in the history
* #340 started looking at supporting automatic routing when using service discovery

* #340 getting old routing tests to pass

* #340 renamed stuff to provider rather than finder, as its not longer finding anything

* #340 working towards supporting dynamic routing

* #340 loads of refactoring to make configuration work with dynamic routing

* #340 refactor consul config code so the registry class owns it

* #340 default to consul to maintain backwards compat

* #340 added docs, finished this branches todos
  • Loading branch information
TomPallister authored May 14, 2018
1 parent dadb43e commit 1e2e953
Show file tree
Hide file tree
Showing 64 changed files with 2,083 additions and 1,222 deletions.
9 changes: 8 additions & 1 deletion docs/features/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,11 @@ and
}
In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have
matched /goods/{catchAll} (because this is the first ReRoute in the list!).
matched /goods/{catchAll} (because this is the first ReRoute in the list!).

Dynamic Routing
^^^^^^^^^^^^^^^

This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing
when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if
this sounds interesting to you.
64 changes: 64 additions & 0 deletions docs/features/servicediscovery.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. service-discovery:
Service Discovery
=================

Expand Down Expand Up @@ -88,3 +90,65 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the
When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code
is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work.

Dynamic Routing
^^^^^^^^^^^^^^^

This feature was requested in `issue 340 <https://github.com/TomPallister/Ocelot/issue/340>`_. The idea is to enable dynamic routing when using
a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segmentof the upstream path to lookup the
downstream service with the service discovery provider.

An example of this would be calling ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of
the path which is product and use it as a key to look up the service in consul. If consul returns a service Ocelot will request it on whatever host and
port comes back from consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products.
Ocelot will apprend any query string to the downstream url as normal.

In order to enable dynamic routing you need to have 0 ReRoutes in your config. At the moment you cannot mix dynamic and configuration ReRoutes. In addition to this you
need to specify the Service Discovery provider details as outlined above and the downstream http/https scheme as DownstreamScheme.

In addition to that you can set RateLimitOptions, QoSOptions, LoadBalancerOptions and HttpHandlerOptions, DownstreamScheme (You might want to call Ocelot on https but
talk to private services over http) that will be applied to all of the dynamic ReRoutes.

The config might look something like

.. code-block:: json
{
"ReRoutes": [],
"Aggregates": [],
"GlobalConfiguration": {
"RequestIdKey": null,
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 8510,
"Type": null,
"Token": null,
"ConfigurationKey": null
},
"RateLimitOptions": {
"ClientIdHeader": "ClientId",
"QuotaExceededMessage": null,
"RateLimitCounterPrefix": "ocelot",
"DisableRateLimitHeaders": false,
"HttpStatusCode": 429
},
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0,
"TimeoutValue": 0
},
"BaseUrl": null,
"LoadBalancerOptions": {
"Type": "LeastConnection",
"Key": null,
"Expiry": 0
},
"DownstreamScheme": "http",
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
"UseCookieContainer": false,
"UseTracing": false
}
}
}
Please take a look through all of the docs to understand these options.
26 changes: 5 additions & 21 deletions src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Ocelot.Configuration.Builder
public class DownstreamReRouteBuilder
{
private AuthenticationOptions _authenticationOptions;
private string _reRouteKey;
private string _loadBalancerKey;
private string _downstreamPathTemplate;
private string _upstreamTemplate;
private UpstreamPathTemplate _upstreamTemplatePattern;
Expand All @@ -25,7 +25,6 @@ public class DownstreamReRouteBuilder
private CacheOptions _fileCacheOptions;
private string _downstreamScheme;
private LoadBalancerOptions _loadBalancerOptions;
private bool _useQos;
private QoSOptions _qosOptions;
private HttpHandlerOptions _httpHandlerOptions;
private bool _enableRateLimiting;
Expand All @@ -41,7 +40,6 @@ public class DownstreamReRouteBuilder
private List<AddHeader> _addHeadersToDownstream;
private List<AddHeader> _addHeadersToUpstream;
private bool _dangerousAcceptAnyServerCertificateValidator;
private string _qosKey;

public DownstreamReRouteBuilder()
{
Expand Down Expand Up @@ -153,27 +151,15 @@ public DownstreamReRouteBuilder WithCacheOptions(CacheOptions input)
return this;
}

public DownstreamReRouteBuilder WithIsQos(bool input)
{
_useQos = input;
return this;
}

public DownstreamReRouteBuilder WithQosOptions(QoSOptions input)
{
_qosOptions = input;
return this;
}

public DownstreamReRouteBuilder WithReRouteKey(string reRouteKey)
{
_reRouteKey = reRouteKey;
return this;
}

public DownstreamReRouteBuilder WithQosKey(string qosKey)
public DownstreamReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
{
_qosKey = qosKey;
_loadBalancerKey = loadBalancerKey;
return this;
}

Expand Down Expand Up @@ -267,7 +253,6 @@ public DownstreamReRoute Build()
_httpHandlerOptions,
_useServiceDiscovery,
_enableRateLimiting,
_useQos,
_qosOptions,
_downstreamScheme,
_requestIdHeaderKey,
Expand All @@ -283,12 +268,11 @@ public DownstreamReRoute Build()
_isAuthorised,
_authenticationOptions,
new PathTemplate(_downstreamPathTemplate),
_reRouteKey,
_loadBalancerKey,
_delegatingHandlers,
_addHeadersToDownstream,
_addHeadersToUpstream,
_dangerousAcceptAnyServerCertificateValidator,
_qosKey);
_dangerousAcceptAnyServerCertificateValidator);
}
}
}
12 changes: 10 additions & 2 deletions src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ public class QoSOptionsBuilder

private int _durationOfBreak;

private int _timeoutValue;
private int _timeoutValue;

private string _key;

public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking)
{
Expand All @@ -26,9 +28,15 @@ public QoSOptionsBuilder WithTimeoutValue(int timeoutValue)
return this;
}

public QoSOptionsBuilder WithKey(string input)
{
_key = input;
return this;
}

public QoSOptions Build()
{
return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue);
return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue, _key);
}
}
}
11 changes: 2 additions & 9 deletions src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ public class ReRouteOptionsBuilder
private bool _isAuthenticated;
private bool _isAuthorised;
private bool _isCached;
private bool _isQoS;
private bool _enableRateLimiting;

public ReRouteOptionsBuilder WithIsCached(bool isCached)
Expand All @@ -26,12 +25,6 @@ public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised)
return this;
}

public ReRouteOptionsBuilder WithIsQos(bool isQoS)
{
_isQoS = isQoS;
return this;
}

public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting)
{
_enableRateLimiting = enableRateLimiting;
Expand All @@ -40,7 +33,7 @@ public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting)

public ReRouteOptions Build()
{
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _isQoS, _enableRateLimiting);
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,22 @@ private async Task<Response<IInternalConfiguration>> SetUpConfiguration(FileConf
}

var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);

var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);

var lbOptions = CreateLoadBalancerOptions(fileConfiguration.GlobalConfiguration.LoadBalancerOptions);

var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions);

var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions);

var config = new InternalConfiguration(reRoutes,
_adminPath.Path,
serviceProviderConfiguration,
fileConfiguration.GlobalConfiguration.RequestIdKey,
lbOptions,
fileConfiguration.GlobalConfiguration.DownstreamScheme,
qosOptions,
httpHandlerOptions
);

return new OkResponse<IInternalConfiguration>(config);
}
Expand Down Expand Up @@ -160,8 +174,6 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl

var reRouteKey = CreateReRouteKey(fileReRoute);

var qosKey = CreateQosKey(fileReRoute);

var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);

var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
Expand All @@ -172,19 +184,19 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl

var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);

var qosOptions = _qosOptionsCreator.Create(fileReRoute);
var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod.ToArray());

var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);

var region = _regionCreator.Create(fileReRoute);

var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions);

var hAndRs = _headerFAndRCreator.Create(fileReRoute);

var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);

var lbOptions = CreateLoadBalancerOptions(fileReRoute);
var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions);

var reRoute = new DownstreamReRouteBuilder()
.WithKey(fileReRoute.Key)
Expand All @@ -205,9 +217,7 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancerOptions(lbOptions)
.WithDownstreamAddresses(downstreamAddresses)
.WithReRouteKey(reRouteKey)
.WithQosKey(qosKey)
.WithIsQos(fileReRouteOptions.IsQos)
.WithLoadBalancerKey(reRouteKey)
.WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption)
Expand All @@ -226,9 +236,13 @@ private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGl
return reRoute;
}

private LoadBalancerOptions CreateLoadBalancerOptions(FileReRoute fileReRoute)
private LoadBalancerOptions CreateLoadBalancerOptions(FileLoadBalancerOptions options)
{
return new LoadBalancerOptions(fileReRoute.LoadBalancerOptions.Type, fileReRoute.LoadBalancerOptions.Key, fileReRoute.LoadBalancerOptions.Expiry);
return new LoadBalancerOptionsBuilder()
.WithType(options.Type)
.WithKey(options.Key)
.WithExpiryInMs(options.Expiry)
.Build();
}

private string CreateReRouteKey(FileReRoute fileReRoute)
Expand All @@ -238,14 +252,7 @@ private string CreateReRouteKey(FileReRoute fileReRoute)
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
}

return CreateQosKey(fileReRoute);
}

private string CreateQosKey(FileReRoute fileReRoute)
{
//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.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
return loadBalancerKey;
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
}
}
}
10 changes: 5 additions & 5 deletions src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ namespace Ocelot.Configuration.Creator
{
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{
private IServiceTracer _tracer;
private readonly IServiceTracer _tracer;

public HttpHandlerOptionsCreator(IServiceTracer tracer)
{
_tracer = tracer;
}

public HttpHandlerOptions Create(FileReRoute fileReRoute)
public HttpHandlerOptions Create(FileHttpHandlerOptions options)
{
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) ? fileReRoute.HttpHandlerOptions.UseTracing : false;
var useTracing = _tracer.GetType() != typeof(FakeServiceTracer) && options.UseTracing;

return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
fileReRoute.HttpHandlerOptions.UseCookieContainer, useTracing);
return new HttpHandlerOptions(options.AllowAutoRedirect,
options.UseCookieContainer, useTracing);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace Ocelot.Configuration.Creator
/// </summary>
public interface IHttpHandlerOptionsCreator
{
HttpHandlerOptions Create(FileReRoute fileReRoute);
HttpHandlerOptions Create(FileHttpHandlerOptions fileReRoute);
}
}
6 changes: 4 additions & 2 deletions src/Ocelot/Configuration/Creator/IQoSOptionsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace Ocelot.Configuration.Creator
{
public interface IQoSOptionsCreator
{
QoSOptions Create(FileReRoute fileReRoute);
QoSOptions Create(FileQoSOptions options);
QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods);
QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods);
}
}
}
2 changes: 1 addition & 1 deletion src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ public interface IReRouteOptionsCreator
{
ReRouteOptions Create(FileReRoute fileReRoute);
}
}
}
Loading

0 comments on commit 1e2e953

Please sign in to comment.