mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-20 00:48:16 +08:00
Feature/sticky sessions (#336)
* started messing around with sticky sessions idea * more tests for sticky session thing * more faffing cant make up my mind how to do this * +semver: breaking added sticky session load balancer and changed way load balancer configuration is set by user * #336 made tests BDDFy
This commit is contained in:
@ -24,7 +24,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private bool _isCached;
|
||||
private CacheOptions _fileCacheOptions;
|
||||
private string _downstreamScheme;
|
||||
private string _loadBalancer;
|
||||
private LoadBalancerOptions _loadBalancerOptions;
|
||||
private bool _useQos;
|
||||
private QoSOptions _qosOptions;
|
||||
private HttpHandlerOptions _httpHandlerOptions;
|
||||
@ -41,6 +41,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private List<AddHeader> _addHeadersToDownstream;
|
||||
private List<AddHeader> _addHeadersToUpstream;
|
||||
private bool _dangerousAcceptAnyServerCertificateValidator;
|
||||
private string _qosKey;
|
||||
|
||||
public DownstreamReRouteBuilder()
|
||||
{
|
||||
@ -62,9 +63,9 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithLoadBalancer(string loadBalancer)
|
||||
public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions)
|
||||
{
|
||||
_loadBalancer = loadBalancer;
|
||||
_loadBalancerOptions = loadBalancerOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -170,6 +171,12 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithQosKey(string qosKey)
|
||||
{
|
||||
_qosKey = qosKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions)
|
||||
{
|
||||
_authenticationOptions = authenticationOptions;
|
||||
@ -266,7 +273,7 @@ namespace Ocelot.Configuration.Builder
|
||||
_requestIdHeaderKey,
|
||||
_isCached,
|
||||
_fileCacheOptions,
|
||||
_loadBalancer,
|
||||
_loadBalancerOptions,
|
||||
_rateLimitOptions,
|
||||
_routeClaimRequirement,
|
||||
_claimToQueries,
|
||||
@ -280,7 +287,8 @@ namespace Ocelot.Configuration.Builder
|
||||
_delegatingHandlers,
|
||||
_addHeadersToDownstream,
|
||||
_addHeadersToUpstream,
|
||||
_dangerousAcceptAnyServerCertificateValidator);
|
||||
_dangerousAcceptAnyServerCertificateValidator,
|
||||
_qosKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,229 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class FileInternalConfigurationCreator : IInternalConfigurationCreator
|
||||
{
|
||||
private readonly IConfigurationValidator _configurationValidator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
||||
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
|
||||
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
|
||||
private readonly IRequestIdKeyCreator _requestIdKeyCreator;
|
||||
private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
|
||||
private readonly IQoSOptionsCreator _qosOptionsCreator;
|
||||
private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
|
||||
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
|
||||
private readonly IRegionCreator _regionCreator;
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private readonly IAdministrationPath _adminPath;
|
||||
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
||||
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
||||
|
||||
public FileInternalConfigurationCreator(
|
||||
IConfigurationValidator configurationValidator,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IClaimsToThingCreator claimsToThingCreator,
|
||||
IAuthenticationOptionsCreator authOptionsCreator,
|
||||
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
|
||||
IRequestIdKeyCreator requestIdKeyCreator,
|
||||
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
|
||||
IQoSOptionsCreator qosOptionsCreator,
|
||||
IReRouteOptionsCreator fileReRouteOptionsCreator,
|
||||
IRateLimitOptionsCreator rateLimitOptionsCreator,
|
||||
IRegionCreator regionCreator,
|
||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
||||
IAdministrationPath adminPath,
|
||||
IHeaderFindAndReplaceCreator headerFAndRCreator,
|
||||
IDownstreamAddressesCreator downstreamAddressesCreator
|
||||
)
|
||||
{
|
||||
_downstreamAddressesCreator = downstreamAddressesCreator;
|
||||
_headerFAndRCreator = headerFAndRCreator;
|
||||
_adminPath = adminPath;
|
||||
_regionCreator = regionCreator;
|
||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
||||
_requestIdKeyCreator = requestIdKeyCreator;
|
||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
||||
_authOptionsCreator = authOptionsCreator;
|
||||
_configurationValidator = configurationValidator;
|
||||
_logger = loggerFactory.CreateLogger<FileInternalConfigurationCreator>();
|
||||
_claimsToThingCreator = claimsToThingCreator;
|
||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
||||
_qosOptionsCreator = qosOptionsCreator;
|
||||
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
|
||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||
}
|
||||
|
||||
public async Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var config = await SetUpConfiguration(fileConfiguration);
|
||||
return config;
|
||||
}
|
||||
|
||||
private async Task<Response<IInternalConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||
|
||||
if (response.Data.IsError)
|
||||
{
|
||||
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
||||
}
|
||||
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||
{
|
||||
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute);
|
||||
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
foreach (var aggregate in fileConfiguration.Aggregates)
|
||||
{
|
||||
var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration);
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);
|
||||
|
||||
return new OkResponse<IInternalConfiguration>(config);
|
||||
}
|
||||
|
||||
public ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var applicableReRoutes = reRoutes
|
||||
.SelectMany(x => x.DownstreamReRoute)
|
||||
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
||||
.ToList();
|
||||
|
||||
if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
||||
{
|
||||
//todo - log or throw or return error whatever?
|
||||
}
|
||||
|
||||
//make another re route out of these
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamPathTemplate(aggregateReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoutes(applicableReRoutes)
|
||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||
.WithAggregator(aggregateReRoute.Aggregator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes)
|
||||
{
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoute(downstreamReRoutes)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration);
|
||||
|
||||
var reRouteKey = CreateReRouteKey(fileReRoute);
|
||||
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
|
||||
|
||||
var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest);
|
||||
|
||||
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
|
||||
|
||||
var region = _regionCreator.Create(fileReRoute);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var hAndRs = _headerFAndRCreator.Create(fileReRoute);
|
||||
|
||||
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithKey(fileReRoute.Key)
|
||||
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)
|
||||
.WithAuthenticationOptions(authOptionsForRoute)
|
||||
.WithClaimsToHeaders(claimsToHeaders)
|
||||
.WithClaimsToClaims(claimsToClaims)
|
||||
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||
.WithIsAuthorised(fileReRouteOptions.IsAuthorised)
|
||||
.WithClaimsToQueries(claimsToQueries)
|
||||
.WithRequestIdKey(requestIdKey)
|
||||
.WithIsCached(fileReRouteOptions.IsCached)
|
||||
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
|
||||
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
|
||||
.WithLoadBalancer(fileReRoute.LoadBalancer)
|
||||
.WithDownstreamAddresses(downstreamAddresses)
|
||||
.WithReRouteKey(reRouteKey)
|
||||
.WithIsQos(fileReRouteOptions.IsQos)
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithHttpHandlerOptions(httpHandlerOptions)
|
||||
.WithServiceName(fileReRoute.ServiceName)
|
||||
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
|
||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private string CreateReRouteKey(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using LoadBalancer.LoadBalancers;
|
||||
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class FileInternalConfigurationCreator : IInternalConfigurationCreator
|
||||
{
|
||||
private readonly IConfigurationValidator _configurationValidator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
||||
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
|
||||
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
|
||||
private readonly IRequestIdKeyCreator _requestIdKeyCreator;
|
||||
private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
|
||||
private readonly IQoSOptionsCreator _qosOptionsCreator;
|
||||
private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
|
||||
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
|
||||
private readonly IRegionCreator _regionCreator;
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private readonly IAdministrationPath _adminPath;
|
||||
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
||||
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
||||
|
||||
public FileInternalConfigurationCreator(
|
||||
IConfigurationValidator configurationValidator,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IClaimsToThingCreator claimsToThingCreator,
|
||||
IAuthenticationOptionsCreator authOptionsCreator,
|
||||
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
|
||||
IRequestIdKeyCreator requestIdKeyCreator,
|
||||
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
|
||||
IQoSOptionsCreator qosOptionsCreator,
|
||||
IReRouteOptionsCreator fileReRouteOptionsCreator,
|
||||
IRateLimitOptionsCreator rateLimitOptionsCreator,
|
||||
IRegionCreator regionCreator,
|
||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
||||
IAdministrationPath adminPath,
|
||||
IHeaderFindAndReplaceCreator headerFAndRCreator,
|
||||
IDownstreamAddressesCreator downstreamAddressesCreator
|
||||
)
|
||||
{
|
||||
_downstreamAddressesCreator = downstreamAddressesCreator;
|
||||
_headerFAndRCreator = headerFAndRCreator;
|
||||
_adminPath = adminPath;
|
||||
_regionCreator = regionCreator;
|
||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
||||
_requestIdKeyCreator = requestIdKeyCreator;
|
||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
||||
_authOptionsCreator = authOptionsCreator;
|
||||
_configurationValidator = configurationValidator;
|
||||
_logger = loggerFactory.CreateLogger<FileInternalConfigurationCreator>();
|
||||
_claimsToThingCreator = claimsToThingCreator;
|
||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
||||
_qosOptionsCreator = qosOptionsCreator;
|
||||
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
|
||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||
}
|
||||
|
||||
public async Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var config = await SetUpConfiguration(fileConfiguration);
|
||||
return config;
|
||||
}
|
||||
|
||||
private async Task<Response<IInternalConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||
|
||||
if (response.Data.IsError)
|
||||
{
|
||||
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
||||
}
|
||||
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||
{
|
||||
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute);
|
||||
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
foreach (var aggregate in fileConfiguration.Aggregates)
|
||||
{
|
||||
var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration);
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var config = new InternalConfiguration(reRoutes, _adminPath.Path, serviceProviderConfiguration, fileConfiguration.GlobalConfiguration.RequestIdKey);
|
||||
|
||||
return new OkResponse<IInternalConfiguration>(config);
|
||||
}
|
||||
|
||||
public ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var applicableReRoutes = reRoutes
|
||||
.SelectMany(x => x.DownstreamReRoute)
|
||||
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
||||
.ToList();
|
||||
|
||||
if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
||||
{
|
||||
//todo - log or throw or return error whatever?
|
||||
}
|
||||
|
||||
//make another re route out of these
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamPathTemplate(aggregateReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoutes(applicableReRoutes)
|
||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||
.WithAggregator(aggregateReRoute.Aggregator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes)
|
||||
{
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoute(downstreamReRoutes)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration);
|
||||
|
||||
var reRouteKey = CreateReRouteKey(fileReRoute);
|
||||
|
||||
var qosKey = CreateQosKey(fileReRoute);
|
||||
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
|
||||
|
||||
var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest);
|
||||
|
||||
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
|
||||
|
||||
var region = _regionCreator.Create(fileReRoute);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var hAndRs = _headerFAndRCreator.Create(fileReRoute);
|
||||
|
||||
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
||||
|
||||
var lbOptions = CreateLoadBalancerOptions(fileReRoute);
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithKey(fileReRoute.Key)
|
||||
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamTemplatePattern(upstreamTemplatePattern)
|
||||
.WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)
|
||||
.WithAuthenticationOptions(authOptionsForRoute)
|
||||
.WithClaimsToHeaders(claimsToHeaders)
|
||||
.WithClaimsToClaims(claimsToClaims)
|
||||
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||
.WithIsAuthorised(fileReRouteOptions.IsAuthorised)
|
||||
.WithClaimsToQueries(claimsToQueries)
|
||||
.WithRequestIdKey(requestIdKey)
|
||||
.WithIsCached(fileReRouteOptions.IsCached)
|
||||
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
|
||||
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
|
||||
.WithLoadBalancerOptions(lbOptions)
|
||||
.WithDownstreamAddresses(downstreamAddresses)
|
||||
.WithReRouteKey(reRouteKey)
|
||||
.WithQosKey(qosKey)
|
||||
.WithIsQos(fileReRouteOptions.IsQos)
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithHttpHandlerOptions(httpHandlerOptions)
|
||||
.WithServiceName(fileReRoute.ServiceName)
|
||||
.WithUseServiceDiscovery(fileReRoute.UseServiceDiscovery)
|
||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private LoadBalancerOptions CreateLoadBalancerOptions(FileReRoute fileReRoute)
|
||||
{
|
||||
return new LoadBalancerOptions(fileReRoute.LoadBalancerOptions.Type, fileReRoute.LoadBalancerOptions.Key, fileReRoute.LoadBalancerOptions.Expiry);
|
||||
}
|
||||
|
||||
private string CreateReRouteKey(FileReRoute fileReRoute)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type) && !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key) && fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Creator;
|
||||
using Values;
|
||||
|
||||
public class DownstreamReRoute
|
||||
{
|
||||
public DownstreamReRoute(
|
||||
string key,
|
||||
PathTemplate upstreamPathTemplate,
|
||||
List<HeaderFindAndReplace> upstreamHeadersFindAndReplace,
|
||||
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
||||
List<DownstreamHostAndPort> downstreamAddresses,
|
||||
string serviceName,
|
||||
HttpHandlerOptions httpHandlerOptions,
|
||||
bool useServiceDiscovery,
|
||||
bool enableEndpointEndpointRateLimiting,
|
||||
bool isQos,
|
||||
QoSOptions qosOptionsOptions,
|
||||
string downstreamScheme,
|
||||
string requestIdKey,
|
||||
bool isCached,
|
||||
CacheOptions cacheOptions,
|
||||
string loadBalancer,
|
||||
RateLimitOptions rateLimitOptions,
|
||||
Dictionary<string, string> routeClaimsRequirement,
|
||||
List<ClaimToThing> claimsToQueries,
|
||||
List<ClaimToThing> claimsToHeaders,
|
||||
List<ClaimToThing> claimsToClaims,
|
||||
bool isAuthenticated,
|
||||
bool isAuthorised,
|
||||
AuthenticationOptions authenticationOptions,
|
||||
PathTemplate downstreamPathTemplate,
|
||||
string reRouteKey,
|
||||
List<HeaderFindAndReplace> downstreamHeadersFindAndReplace,
|
||||
List<DownstreamHostAndPort> downstreamAddresses,
|
||||
string serviceName,
|
||||
HttpHandlerOptions httpHandlerOptions,
|
||||
bool useServiceDiscovery,
|
||||
bool enableEndpointEndpointRateLimiting,
|
||||
bool isQos,
|
||||
QoSOptions qosOptionsOptions,
|
||||
string downstreamScheme,
|
||||
string requestIdKey,
|
||||
bool isCached,
|
||||
CacheOptions cacheOptions,
|
||||
LoadBalancerOptions loadBalancerOptions,
|
||||
RateLimitOptions rateLimitOptions,
|
||||
Dictionary<string, string> routeClaimsRequirement,
|
||||
List<ClaimToThing> claimsToQueries,
|
||||
List<ClaimToThing> claimsToHeaders,
|
||||
List<ClaimToThing> claimsToClaims,
|
||||
bool isAuthenticated,
|
||||
bool isAuthorised,
|
||||
AuthenticationOptions authenticationOptions,
|
||||
PathTemplate downstreamPathTemplate,
|
||||
string loadBalancerKey,
|
||||
List<string> delegatingHandlers,
|
||||
List<AddHeader> addHeadersToDownstream,
|
||||
List<AddHeader> addHeadersToUpstream,
|
||||
bool dangerousAcceptAnyServerCertificateValidator)
|
||||
bool dangerousAcceptAnyServerCertificateValidator,
|
||||
string qosKey)
|
||||
{
|
||||
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
||||
AddHeadersToDownstream = addHeadersToDownstream;
|
||||
@ -56,7 +57,7 @@ namespace Ocelot.Configuration
|
||||
RequestIdKey = requestIdKey;
|
||||
IsCached = isCached;
|
||||
CacheOptions = cacheOptions;
|
||||
LoadBalancer = loadBalancer;
|
||||
LoadBalancerOptions = loadBalancerOptions;
|
||||
RateLimitOptions = rateLimitOptions;
|
||||
RouteClaimsRequirement = routeClaimsRequirement;
|
||||
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
|
||||
@ -66,39 +67,41 @@ namespace Ocelot.Configuration
|
||||
IsAuthorised = isAuthorised;
|
||||
AuthenticationOptions = authenticationOptions;
|
||||
DownstreamPathTemplate = downstreamPathTemplate;
|
||||
ReRouteKey = reRouteKey;
|
||||
LoadBalancerKey = loadBalancerKey;
|
||||
AddHeadersToUpstream = addHeadersToUpstream;
|
||||
QosKey = qosKey;
|
||||
}
|
||||
|
||||
public string Key { get; private set; }
|
||||
public PathTemplate UpstreamPathTemplate { get;private set; }
|
||||
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace {get;private set;}
|
||||
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; private set; }
|
||||
public List<DownstreamHostAndPort> DownstreamAddresses { get; private set; }
|
||||
public string ServiceName { get; private set; }
|
||||
public HttpHandlerOptions HttpHandlerOptions { get; private set; }
|
||||
public bool UseServiceDiscovery { get; private set; }
|
||||
public bool EnableEndpointEndpointRateLimiting { get; private set; }
|
||||
public bool IsQos { get; private set; }
|
||||
public QoSOptions QosOptionsOptions { get; private set; }
|
||||
public string DownstreamScheme { get; private set; }
|
||||
public string RequestIdKey { get; private set; }
|
||||
public bool IsCached { get; private set; }
|
||||
public CacheOptions CacheOptions { get; private set; }
|
||||
public string LoadBalancer { get; private set; }
|
||||
public RateLimitOptions RateLimitOptions { get; private set; }
|
||||
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
|
||||
public List<ClaimToThing> ClaimsToQueries { get; private set; }
|
||||
public List<ClaimToThing> ClaimsToHeaders { get; private set; }
|
||||
public List<ClaimToThing> ClaimsToClaims { get; private set; }
|
||||
public bool IsAuthenticated { get; private set; }
|
||||
public bool IsAuthorised { get; private set; }
|
||||
public AuthenticationOptions AuthenticationOptions { get; private set; }
|
||||
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||
public string ReRouteKey { get; private set; }
|
||||
public List<string> DelegatingHandlers {get;private set;}
|
||||
public List<AddHeader> AddHeadersToDownstream {get;private set;}
|
||||
public List<AddHeader> AddHeadersToUpstream { get; private set; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; private set; }
|
||||
public string QosKey { get; }
|
||||
public string Key { get; }
|
||||
public PathTemplate UpstreamPathTemplate { get; }
|
||||
public List<HeaderFindAndReplace> UpstreamHeadersFindAndReplace { get; }
|
||||
public List<HeaderFindAndReplace> DownstreamHeadersFindAndReplace { get; }
|
||||
public List<DownstreamHostAndPort> DownstreamAddresses { get; }
|
||||
public string ServiceName { get; }
|
||||
public HttpHandlerOptions HttpHandlerOptions { get; }
|
||||
public bool UseServiceDiscovery { get; }
|
||||
public bool EnableEndpointEndpointRateLimiting { get; }
|
||||
public bool IsQos { get; }
|
||||
public QoSOptions QosOptionsOptions { get; }
|
||||
public string DownstreamScheme { get; }
|
||||
public string RequestIdKey { get; }
|
||||
public bool IsCached { get; }
|
||||
public CacheOptions CacheOptions { get; }
|
||||
public LoadBalancerOptions LoadBalancerOptions { get; }
|
||||
public RateLimitOptions RateLimitOptions { get; }
|
||||
public Dictionary<string, string> RouteClaimsRequirement { get; }
|
||||
public List<ClaimToThing> ClaimsToQueries { get; }
|
||||
public List<ClaimToThing> ClaimsToHeaders { get; }
|
||||
public List<ClaimToThing> ClaimsToClaims { get; }
|
||||
public bool IsAuthenticated { get; }
|
||||
public bool IsAuthorised { get; }
|
||||
public AuthenticationOptions AuthenticationOptions { get; }
|
||||
public PathTemplate DownstreamPathTemplate { get; }
|
||||
public string LoadBalancerKey { get; }
|
||||
public List<string> DelegatingHandlers { get; }
|
||||
public List<AddHeader> AddHeadersToDownstream { get; }
|
||||
public List<AddHeader> AddHeadersToUpstream { get; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; }
|
||||
}
|
||||
}
|
||||
|
9
src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs
Normal file
9
src/Ocelot/Configuration/File/FileLoadBalancerOptions.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Ocelot.Configuration.File
|
||||
{
|
||||
public class FileLoadBalancerOptions
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Key { get; set; }
|
||||
public int Expiry { get; set; }
|
||||
}
|
||||
}
|
@ -1,54 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.Configuration.File
|
||||
{
|
||||
public class FileReRoute : IReRoute
|
||||
{
|
||||
public FileReRoute()
|
||||
{
|
||||
UpstreamHttpMethod = new List<string>();
|
||||
AddHeadersToRequest = new Dictionary<string, string>();
|
||||
AddClaimsToRequest = new Dictionary<string, string>();
|
||||
RouteClaimsRequirement = new Dictionary<string, string>();
|
||||
AddQueriesToRequest = new Dictionary<string, string>();
|
||||
DownstreamHeaderTransform = new Dictionary<string, string>();
|
||||
FileCacheOptions = new FileCacheOptions();
|
||||
QoSOptions = new FileQoSOptions();
|
||||
RateLimitOptions = new FileRateLimitRule();
|
||||
AuthenticationOptions = new FileAuthenticationOptions();
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions();
|
||||
UpstreamHeaderTransform = new Dictionary<string, string>();
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
||||
DelegatingHandlers = new List<string>();
|
||||
Priority = 1;
|
||||
}
|
||||
|
||||
public string DownstreamPathTemplate { get; set; }
|
||||
public string UpstreamPathTemplate { get; set; }
|
||||
public List<string> UpstreamHttpMethod { get; set; }
|
||||
public Dictionary<string, string> AddHeadersToRequest { get; set; }
|
||||
public Dictionary<string, string> UpstreamHeaderTransform { get; set; }
|
||||
public Dictionary<string, string> DownstreamHeaderTransform { get; set; }
|
||||
public Dictionary<string, string> AddClaimsToRequest { get; set; }
|
||||
public Dictionary<string, string> RouteClaimsRequirement { get; set; }
|
||||
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
||||
public string RequestIdKey { get; set; }
|
||||
public FileCacheOptions FileCacheOptions { get; set; }
|
||||
public bool ReRouteIsCaseSensitive { get; set; }
|
||||
public string ServiceName { get; set; }
|
||||
public string DownstreamScheme {get;set;}
|
||||
public FileQoSOptions QoSOptions { get; set; }
|
||||
public string LoadBalancer { get;set; }
|
||||
public FileRateLimitRule RateLimitOptions { get; set; }
|
||||
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
||||
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
||||
public bool UseServiceDiscovery { get;set; }
|
||||
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
||||
public string UpstreamHost { get; set; }
|
||||
public string Key { get;set; }
|
||||
public List<string> DelegatingHandlers {get;set;}
|
||||
public int Priority { get;set; }
|
||||
public int Timeout { get; set; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.Configuration.File
|
||||
{
|
||||
public class FileReRoute : IReRoute
|
||||
{
|
||||
public FileReRoute()
|
||||
{
|
||||
UpstreamHttpMethod = new List<string>();
|
||||
AddHeadersToRequest = new Dictionary<string, string>();
|
||||
AddClaimsToRequest = new Dictionary<string, string>();
|
||||
RouteClaimsRequirement = new Dictionary<string, string>();
|
||||
AddQueriesToRequest = new Dictionary<string, string>();
|
||||
DownstreamHeaderTransform = new Dictionary<string, string>();
|
||||
FileCacheOptions = new FileCacheOptions();
|
||||
QoSOptions = new FileQoSOptions();
|
||||
RateLimitOptions = new FileRateLimitRule();
|
||||
AuthenticationOptions = new FileAuthenticationOptions();
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions();
|
||||
UpstreamHeaderTransform = new Dictionary<string, string>();
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
||||
DelegatingHandlers = new List<string>();
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions();
|
||||
Priority = 1;
|
||||
}
|
||||
|
||||
public string DownstreamPathTemplate { get; set; }
|
||||
public string UpstreamPathTemplate { get; set; }
|
||||
public List<string> UpstreamHttpMethod { get; set; }
|
||||
public Dictionary<string, string> AddHeadersToRequest { get; set; }
|
||||
public Dictionary<string, string> UpstreamHeaderTransform { get; set; }
|
||||
public Dictionary<string, string> DownstreamHeaderTransform { get; set; }
|
||||
public Dictionary<string, string> AddClaimsToRequest { get; set; }
|
||||
public Dictionary<string, string> RouteClaimsRequirement { get; set; }
|
||||
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
||||
public string RequestIdKey { get; set; }
|
||||
public FileCacheOptions FileCacheOptions { get; set; }
|
||||
public bool ReRouteIsCaseSensitive { get; set; }
|
||||
public string ServiceName { get; set; }
|
||||
public string DownstreamScheme {get;set;}
|
||||
public FileQoSOptions QoSOptions { get; set; }
|
||||
public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
|
||||
public FileRateLimitRule RateLimitOptions { get; set; }
|
||||
public FileAuthenticationOptions AuthenticationOptions { get; set; }
|
||||
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
||||
public bool UseServiceDiscovery { get;set; }
|
||||
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
|
||||
public string UpstreamHost { get; set; }
|
||||
public string Key { get;set; }
|
||||
public List<string> DelegatingHandlers {get;set;}
|
||||
public int Priority { get;set; }
|
||||
public int Timeout { get; set; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
|
||||
}
|
||||
}
|
||||
|
18
src/Ocelot/Configuration/LoadBalancerOptions.cs
Normal file
18
src/Ocelot/Configuration/LoadBalancerOptions.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
public class LoadBalancerOptions
|
||||
{
|
||||
public LoadBalancerOptions(string type, string key, int expiryInMs)
|
||||
{
|
||||
Type = type;
|
||||
Key = key;
|
||||
ExpiryInMs = expiryInMs;
|
||||
}
|
||||
|
||||
public string Type { get; }
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public int ExpiryInMs { get; }
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Middleware;
|
||||
using Responses;
|
||||
using Values;
|
||||
|
||||
public class CookieStickySessions : ILoadBalancer, IDisposable
|
||||
{
|
||||
private readonly int _expiryInMs;
|
||||
private readonly string _key;
|
||||
private readonly ILoadBalancer _loadBalancer;
|
||||
private readonly ConcurrentDictionary<string, StickySession> _stored;
|
||||
private readonly Timer _timer;
|
||||
private bool _expiring;
|
||||
|
||||
public CookieStickySessions(ILoadBalancer loadBalancer, string key, int expiryInMs)
|
||||
{
|
||||
_key = key;
|
||||
_expiryInMs = expiryInMs;
|
||||
_loadBalancer = loadBalancer;
|
||||
_stored = new ConcurrentDictionary<string, StickySession>();
|
||||
_timer = new Timer(x =>
|
||||
{
|
||||
if (_expiring)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_expiring = true;
|
||||
|
||||
Expire();
|
||||
|
||||
_expiring = false;
|
||||
}, null, 0, 50);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context)
|
||||
{
|
||||
var value = context.HttpContext.Request.Cookies[_key];
|
||||
|
||||
if (!string.IsNullOrEmpty(value) && _stored.ContainsKey(value))
|
||||
{
|
||||
var cached = _stored[value];
|
||||
|
||||
var updated = new StickySession(cached.HostAndPort, DateTime.UtcNow.AddMilliseconds(_expiryInMs));
|
||||
|
||||
_stored[value] = updated;
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(updated.HostAndPort);
|
||||
}
|
||||
|
||||
var next = await _loadBalancer.Lease(context);
|
||||
|
||||
if (next.IsError)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(next.Errors);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(value) && !_stored.ContainsKey(value))
|
||||
{
|
||||
_stored[value] = new StickySession(next.Data, DateTime.UtcNow.AddMilliseconds(_expiryInMs));
|
||||
}
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(next.Data);
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
}
|
||||
|
||||
private void Expire()
|
||||
{
|
||||
var expired = _stored.Where(x => x.Value.Expiry < DateTime.UtcNow);
|
||||
|
||||
foreach (var expire in expired)
|
||||
{
|
||||
_stored.Remove(expire.Key, out _);
|
||||
_loadBalancer.Release(expire.Value.HostAndPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancer
|
||||
{
|
||||
Task<Response<ServiceHostAndPort>> Lease();
|
||||
void Release(ServiceHostAndPort hostAndPort);
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancer
|
||||
{
|
||||
Task<Response<ServiceHostAndPort>> Lease(DownstreamContext context);
|
||||
void Release(ServiceHostAndPort hostAndPort);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancerFactory
|
||||
{
|
||||
Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancerFactory
|
||||
{
|
||||
Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancerHouse
|
||||
{
|
||||
Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public interface ILoadBalancerHouse
|
||||
{
|
||||
Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config);
|
||||
}
|
||||
}
|
||||
|
@ -1,145 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LeastConnection : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
private readonly List<Lease> _leases;
|
||||
private readonly string _serviceName;
|
||||
private static readonly object _syncLock = new object();
|
||||
|
||||
public LeastConnection(Func<Task<List<Service>>> services, string serviceName)
|
||||
{
|
||||
_services = services;
|
||||
_serviceName = serviceName;
|
||||
_leases = new List<Lease>();
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease()
|
||||
{
|
||||
var services = await _services.Invoke();
|
||||
|
||||
if (services == null)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreNullError($"services were null for {_serviceName}") );
|
||||
}
|
||||
|
||||
if (!services.Any())
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError($"services were empty for {_serviceName}"));
|
||||
}
|
||||
|
||||
lock(_syncLock)
|
||||
{
|
||||
//todo - maybe this should be moved somewhere else...? Maybe on a repeater on seperate thread? loop every second and update or something?
|
||||
UpdateServices(services);
|
||||
|
||||
var leaseWithLeastConnections = GetLeaseWithLeastConnections();
|
||||
|
||||
_leases.Remove(leaseWithLeastConnections);
|
||||
|
||||
leaseWithLeastConnections = AddConnection(leaseWithLeastConnections);
|
||||
|
||||
_leases.Add(leaseWithLeastConnections);
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort));
|
||||
}
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
lock(_syncLock)
|
||||
{
|
||||
var matchingLease = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == hostAndPort.DownstreamHost
|
||||
&& l.HostAndPort.DownstreamPort == hostAndPort.DownstreamPort);
|
||||
|
||||
if (matchingLease != null)
|
||||
{
|
||||
var replacementLease = new Lease(hostAndPort, matchingLease.Connections - 1);
|
||||
|
||||
_leases.Remove(matchingLease);
|
||||
|
||||
_leases.Add(replacementLease);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Lease AddConnection(Lease lease)
|
||||
{
|
||||
return new Lease(lease.HostAndPort, lease.Connections + 1);
|
||||
}
|
||||
|
||||
private Lease GetLeaseWithLeastConnections()
|
||||
{
|
||||
//now get the service with the least connections?
|
||||
Lease leaseWithLeastConnections = null;
|
||||
|
||||
for (var i = 0; i < _leases.Count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
leaseWithLeastConnections = _leases[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_leases[i].Connections < leaseWithLeastConnections.Connections)
|
||||
{
|
||||
leaseWithLeastConnections = _leases[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leaseWithLeastConnections;
|
||||
}
|
||||
|
||||
private Response UpdateServices(List<Service> services)
|
||||
{
|
||||
if (_leases.Count > 0)
|
||||
{
|
||||
var leasesToRemove = new List<Lease>();
|
||||
|
||||
foreach (var lease in _leases)
|
||||
{
|
||||
var match = services.FirstOrDefault(s => s.HostAndPort.DownstreamHost == lease.HostAndPort.DownstreamHost
|
||||
&& s.HostAndPort.DownstreamPort == lease.HostAndPort.DownstreamPort);
|
||||
|
||||
if (match == null)
|
||||
{
|
||||
leasesToRemove.Add(lease);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var lease in leasesToRemove)
|
||||
{
|
||||
_leases.Remove(lease);
|
||||
}
|
||||
|
||||
foreach (var service in services)
|
||||
{
|
||||
var exists = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == service.HostAndPort.DownstreamHost && l.HostAndPort.DownstreamPort == service.HostAndPort.DownstreamPort);
|
||||
|
||||
if (exists == null)
|
||||
{
|
||||
_leases.Add(new Lease(service.HostAndPort, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var service in services)
|
||||
{
|
||||
_leases.Add(new Lease(service.HostAndPort, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return new OkResponse();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LeastConnection : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
private readonly List<Lease> _leases;
|
||||
private readonly string _serviceName;
|
||||
private static readonly object _syncLock = new object();
|
||||
|
||||
public LeastConnection(Func<Task<List<Service>>> services, string serviceName)
|
||||
{
|
||||
_services = services;
|
||||
_serviceName = serviceName;
|
||||
_leases = new List<Lease>();
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
{
|
||||
var services = await _services.Invoke();
|
||||
|
||||
if (services == null)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreNullError($"services were null for {_serviceName}") );
|
||||
}
|
||||
|
||||
if (!services.Any())
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError($"services were empty for {_serviceName}"));
|
||||
}
|
||||
|
||||
lock(_syncLock)
|
||||
{
|
||||
//todo - maybe this should be moved somewhere else...? Maybe on a repeater on seperate thread? loop every second and update or something?
|
||||
UpdateServices(services);
|
||||
|
||||
var leaseWithLeastConnections = GetLeaseWithLeastConnections();
|
||||
|
||||
_leases.Remove(leaseWithLeastConnections);
|
||||
|
||||
leaseWithLeastConnections = AddConnection(leaseWithLeastConnections);
|
||||
|
||||
_leases.Add(leaseWithLeastConnections);
|
||||
|
||||
return new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort(leaseWithLeastConnections.HostAndPort.DownstreamHost, leaseWithLeastConnections.HostAndPort.DownstreamPort));
|
||||
}
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
lock(_syncLock)
|
||||
{
|
||||
var matchingLease = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == hostAndPort.DownstreamHost
|
||||
&& l.HostAndPort.DownstreamPort == hostAndPort.DownstreamPort);
|
||||
|
||||
if (matchingLease != null)
|
||||
{
|
||||
var replacementLease = new Lease(hostAndPort, matchingLease.Connections - 1);
|
||||
|
||||
_leases.Remove(matchingLease);
|
||||
|
||||
_leases.Add(replacementLease);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Lease AddConnection(Lease lease)
|
||||
{
|
||||
return new Lease(lease.HostAndPort, lease.Connections + 1);
|
||||
}
|
||||
|
||||
private Lease GetLeaseWithLeastConnections()
|
||||
{
|
||||
//now get the service with the least connections?
|
||||
Lease leaseWithLeastConnections = null;
|
||||
|
||||
for (var i = 0; i < _leases.Count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
leaseWithLeastConnections = _leases[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_leases[i].Connections < leaseWithLeastConnections.Connections)
|
||||
{
|
||||
leaseWithLeastConnections = _leases[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leaseWithLeastConnections;
|
||||
}
|
||||
|
||||
private Response UpdateServices(List<Service> services)
|
||||
{
|
||||
if (_leases.Count > 0)
|
||||
{
|
||||
var leasesToRemove = new List<Lease>();
|
||||
|
||||
foreach (var lease in _leases)
|
||||
{
|
||||
var match = services.FirstOrDefault(s => s.HostAndPort.DownstreamHost == lease.HostAndPort.DownstreamHost
|
||||
&& s.HostAndPort.DownstreamPort == lease.HostAndPort.DownstreamPort);
|
||||
|
||||
if (match == null)
|
||||
{
|
||||
leasesToRemove.Add(lease);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var lease in leasesToRemove)
|
||||
{
|
||||
_leases.Remove(lease);
|
||||
}
|
||||
|
||||
foreach (var service in services)
|
||||
{
|
||||
var exists = _leases.FirstOrDefault(l => l.HostAndPort.DownstreamHost == service.HostAndPort.DownstreamHost && l.HostAndPort.DownstreamPort == service.HostAndPort.DownstreamPort);
|
||||
|
||||
if (exists == null)
|
||||
{
|
||||
_leases.Add(new Lease(service.HostAndPort, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var service in services)
|
||||
{
|
||||
_leases.Add(new Lease(service.HostAndPort, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return new OkResponse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,34 @@
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LoadBalancerFactory : ILoadBalancerFactory
|
||||
{
|
||||
private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory;
|
||||
public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory)
|
||||
{
|
||||
_serviceProviderFactory = serviceProviderFactory;
|
||||
}
|
||||
|
||||
public async Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||
{
|
||||
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
|
||||
|
||||
switch (reRoute.LoadBalancer)
|
||||
{
|
||||
case "RoundRobin":
|
||||
return new RoundRobin(async () => await serviceProvider.Get());
|
||||
case "LeastConnection":
|
||||
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
|
||||
default:
|
||||
return new NoLoadBalancer(await serviceProvider.Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LoadBalancerFactory : ILoadBalancerFactory
|
||||
{
|
||||
private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory;
|
||||
|
||||
public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory)
|
||||
{
|
||||
_serviceProviderFactory = serviceProviderFactory;
|
||||
}
|
||||
|
||||
public async Task<ILoadBalancer> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||
{
|
||||
var serviceProvider = _serviceProviderFactory.Get(config, reRoute);
|
||||
|
||||
switch (reRoute.LoadBalancerOptions?.Type)
|
||||
{
|
||||
case nameof(RoundRobin):
|
||||
return new RoundRobin(async () => await serviceProvider.Get());
|
||||
case nameof(LeastConnection):
|
||||
return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName);
|
||||
case nameof(CookieStickySessions):
|
||||
var loadBalancer = new RoundRobin(async () => await serviceProvider.Get());
|
||||
return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs);
|
||||
default:
|
||||
return new NoLoadBalancer(await serviceProvider.Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LoadBalancerHouse : ILoadBalancerHouse
|
||||
{
|
||||
private readonly ILoadBalancerFactory _factory;
|
||||
private readonly ConcurrentDictionary<string, ILoadBalancer> _loadBalancers;
|
||||
|
||||
public LoadBalancerHouse(ILoadBalancerFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_loadBalancers = new ConcurrentDictionary<string, ILoadBalancer>();
|
||||
}
|
||||
|
||||
public async Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(_loadBalancers.TryGetValue(reRoute.ReRouteKey, out var loadBalancer))
|
||||
{
|
||||
loadBalancer = _loadBalancers[reRoute.ReRouteKey];
|
||||
|
||||
if(reRoute.LoadBalancer != loadBalancer.GetType().Name)
|
||||
{
|
||||
loadBalancer = await _factory.Get(reRoute, config);
|
||||
AddLoadBalancer(reRoute.ReRouteKey, loadBalancer);
|
||||
}
|
||||
|
||||
return new OkResponse<ILoadBalancer>(loadBalancer);
|
||||
}
|
||||
|
||||
loadBalancer = await _factory.Get(reRoute, config);
|
||||
AddLoadBalancer(reRoute.ReRouteKey, loadBalancer);
|
||||
return new OkResponse<ILoadBalancer>(loadBalancer);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
|
||||
{
|
||||
new UnableToFindLoadBalancerError($"unabe to find load balancer for {reRoute.ReRouteKey} exception is {ex}")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLoadBalancer(string key, ILoadBalancer loadBalancer)
|
||||
{
|
||||
_loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class LoadBalancerHouse : ILoadBalancerHouse
|
||||
{
|
||||
private readonly ILoadBalancerFactory _factory;
|
||||
private readonly ConcurrentDictionary<string, ILoadBalancer> _loadBalancers;
|
||||
|
||||
public LoadBalancerHouse(ILoadBalancerFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_loadBalancers = new ConcurrentDictionary<string, ILoadBalancer>();
|
||||
}
|
||||
|
||||
public async Task<Response<ILoadBalancer>> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(_loadBalancers.TryGetValue(reRoute.LoadBalancerKey, out var loadBalancer))
|
||||
{
|
||||
loadBalancer = _loadBalancers[reRoute.LoadBalancerKey];
|
||||
|
||||
if(reRoute.LoadBalancerOptions.Type != loadBalancer.GetType().Name)
|
||||
{
|
||||
loadBalancer = await _factory.Get(reRoute, config);
|
||||
AddLoadBalancer(reRoute.LoadBalancerKey, loadBalancer);
|
||||
}
|
||||
|
||||
return new OkResponse<ILoadBalancer>(loadBalancer);
|
||||
}
|
||||
|
||||
loadBalancer = await _factory.Get(reRoute, config);
|
||||
AddLoadBalancer(reRoute.LoadBalancerKey, loadBalancer);
|
||||
return new OkResponse<ILoadBalancer>(loadBalancer);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
|
||||
{
|
||||
new UnableToFindLoadBalancerError($"unabe to find load balancer for {reRoute.LoadBalancerKey} exception is {ex}")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLoadBalancer(string key, ILoadBalancer loadBalancer)
|
||||
{
|
||||
_loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class NoLoadBalancer : ILoadBalancer
|
||||
{
|
||||
private readonly List<Service> _services;
|
||||
|
||||
public NoLoadBalancer(List<Service> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease()
|
||||
{
|
||||
//todo no point spinning a task up here, also first or default could be null..
|
||||
if (_services == null || _services.Count == 0)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));
|
||||
}
|
||||
|
||||
var service = await Task.FromResult(_services.FirstOrDefault());
|
||||
return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class NoLoadBalancer : ILoadBalancer
|
||||
{
|
||||
private readonly List<Service> _services;
|
||||
|
||||
public NoLoadBalancer(List<Service> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
{
|
||||
//todo no point spinning a task up here, also first or default could be null..
|
||||
if (_services == null || _services.Count == 0)
|
||||
{
|
||||
return new ErrorResponse<ServiceHostAndPort>(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));
|
||||
}
|
||||
|
||||
var service = await Task.FromResult(_services.FirstOrDefault());
|
||||
return new OkResponse<ServiceHostAndPort>(service.HostAndPort);
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class RoundRobin : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
|
||||
private int _last;
|
||||
|
||||
public RoundRobin(Func<Task<List<Service>>> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease()
|
||||
{
|
||||
var services = await _services.Invoke();
|
||||
if (_last >= services.Count)
|
||||
{
|
||||
_last = 0;
|
||||
}
|
||||
|
||||
var next = await Task.FromResult(services[_last]);
|
||||
_last++;
|
||||
return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class RoundRobin : ILoadBalancer
|
||||
{
|
||||
private readonly Func<Task<List<Service>>> _services;
|
||||
|
||||
private int _last;
|
||||
|
||||
public RoundRobin(Func<Task<List<Service>>> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
|
||||
{
|
||||
var services = await _services.Invoke();
|
||||
if (_last >= services.Count)
|
||||
{
|
||||
_last = 0;
|
||||
}
|
||||
|
||||
var next = await Task.FromResult(services[_last]);
|
||||
_last++;
|
||||
return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
|
||||
}
|
||||
|
||||
public void Release(ServiceHostAndPort hostAndPort)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/Ocelot/LoadBalancer/LoadBalancers/StickySession.cs
Normal file
18
src/Ocelot/LoadBalancer/LoadBalancers/StickySession.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
public class StickySession
|
||||
{
|
||||
public StickySession(ServiceHostAndPort hostAndPort, DateTime expiry)
|
||||
{
|
||||
HostAndPort = hostAndPort;
|
||||
Expiry = expiry;
|
||||
}
|
||||
|
||||
public ServiceHostAndPort HostAndPort { get; }
|
||||
|
||||
public DateTime Expiry { get; }
|
||||
}
|
||||
}
|
@ -1,63 +1,63 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
public class LoadBalancingMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||
|
||||
public LoadBalancingMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
ILoadBalancerHouse loadBalancerHouse)
|
||||
:base(loggerFactory.CreateLogger<LoadBalancingMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_loadBalancerHouse = loadBalancerHouse;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration);
|
||||
if(loadBalancer.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, loadBalancer.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostAndPort = await loadBalancer.Data.Lease();
|
||||
if(hostAndPort.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, hostAndPort.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogDebug("Exception calling next middleware, exception will be thrown to global handler");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadBalancer.Data.Release(hostAndPort.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
public class LoadBalancingMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly ILoadBalancerHouse _loadBalancerHouse;
|
||||
|
||||
public LoadBalancingMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
ILoadBalancerHouse loadBalancerHouse)
|
||||
:base(loggerFactory.CreateLogger<LoadBalancingMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_loadBalancerHouse = loadBalancerHouse;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var loadBalancer = await _loadBalancerHouse.Get(context.DownstreamReRoute, context.ServiceProviderConfiguration);
|
||||
if(loadBalancer.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, loadBalancer.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostAndPort = await loadBalancer.Data.Lease(context);
|
||||
if(hostAndPort.IsError)
|
||||
{
|
||||
Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
|
||||
SetPipelineError(context, hostAndPort.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogDebug("Exception calling next middleware, exception will be thrown to global handler");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadBalancer.Data.Release(hostAndPort.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,26 +21,26 @@ namespace Ocelot.Requester.QoS
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_qoSProviders.TryGetValue(reRoute.ReRouteKey, out var qosProvider))
|
||||
if (_qoSProviders.TryGetValue(reRoute.QosKey, out var qosProvider))
|
||||
{
|
||||
if (reRoute.IsQos && qosProvider.CircuitBreaker == null)
|
||||
{
|
||||
qosProvider = _qoSProviderFactory.Get(reRoute);
|
||||
Add(reRoute.ReRouteKey, qosProvider);
|
||||
Add(reRoute.QosKey, qosProvider);
|
||||
}
|
||||
|
||||
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.ReRouteKey]);
|
||||
return new OkResponse<IQoSProvider>(_qoSProviders[reRoute.QosKey]);
|
||||
}
|
||||
|
||||
qosProvider = _qoSProviderFactory.Get(reRoute);
|
||||
Add(reRoute.ReRouteKey, qosProvider);
|
||||
Add(reRoute.QosKey, qosProvider);
|
||||
return new OkResponse<IQoSProvider>(qosProvider);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse<IQoSProvider>(new List<Ocelot.Errors.Error>()
|
||||
{
|
||||
new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.ReRouteKey}, exception was {ex}")
|
||||
new UnableToFindQoSProviderError($"unabe to find qos provider for {reRoute.QosKey}, exception was {ex}")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user