public static void ConfigureRateLimiting(HttpFilterCollection filters) { var rateLimitingPolicyParametersProvider = new SampleRateLimitingClientPolicyProvider(); var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager(rateLimitingPolicyParametersProvider) .AddPathToWhiteList("/api/unlimited") .AddPoliciesForAllEndpoints(new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(100, RateLimitUnit.PerMinute) }, allowAttributeOverride: true, name: "StaticPolicy_2") .AddEndpointPolicy("/api/globallylimited/{id}", "GET", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(5, RateLimitUnit.PerMinute), new AllowedConsumptionRate(8, RateLimitUnit.PerHour) }, true, "StaticPolicy_0") .AddEndpointPolicy("/api/globallylimited/{id}/sub/{subid}", RateLimitPolicy.AllHttpMethods, new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerMinute) }, true, "StaticPolicy_1"); #region Setting up the Redis rate limiter var redisRateLimiterSettings = new RedisRateLimiterSettings(); ConfigureRateLimitingSettings(redisRateLimiterSettings); var rateLimitCacheProvider = new SlidingTimeWindowRateLimiter( redisRateLimiterSettings.RateLimitRedisCacheConnectionString, onThrottled: (rateLimitingResult) => { //_logger.LogInformation( // "Request throttled for client {ClientId} and endpoint {Endpoint}", // rateLimitingResult.CacheKey.RequestId, // rateLimitingResult.CacheKey.RouteTemplate); }, circuitBreaker: new DefaultCircuitBreaker(redisRateLimiterSettings.FaultThreshholdPerWindowDuration, redisRateLimiterSettings.FaultWindowDurationInMilliseconds, redisRateLimiterSettings.CircuitOpenIntervalInSecs, onCircuitOpened: () => { //_logger.LogWarning("Rate limiting circuit opened") }, onCircuitClosed: () => { //logger.LogWarning("Rate limiting circuit closed") })); #endregion filters.Add(new RateLimitingFilter(new RateLimiter(rateLimitCacheProvider, globalRateLimitingClientPolicyManager), filters)); }
private static void ConfigureRateLimitingSettings(RedisRateLimiterSettings redisRateLimiterSettings) { redisRateLimiterSettings.RateLimitRedisCacheConnectionString = ConfigurationManager.AppSettings["RedisRateLimiterSettings:RateLimitRedisCacheConnectionString"]; redisRateLimiterSettings.CircuitOpenIntervalInSecs = Int32.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:CircuitOpenIntervalInSecs"]); redisRateLimiterSettings.ConnectionTimeoutInMilliseconds = Int32.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:ConnectionTimeoutInMilliseconds"]); redisRateLimiterSettings.SyncTimeoutInMilliseconds = Int32.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:SyncTimeoutInMilliseconds"]); redisRateLimiterSettings.FaultThreshholdPerWindowDuration = Int32.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:FaultThreshholdPerWindowDuration"]); redisRateLimiterSettings.FaultWindowDurationInMilliseconds = Int32.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:FaultWindowDurationInMilliseconds"]); redisRateLimiterSettings.CountThrottledRequests = Boolean.Parse(ConfigurationManager.AppSettings["RedisRateLimiterSettings:CountThrottledRequests"]); }
public static void ConfigureRateLimiting(HttpFilterCollection filters) { #region Setting up the Redis rate limiter var redisRateLimiterSettings = new RedisRateLimiterSettings(); ConfigureRateLimitingSettings(redisRateLimiterSettings); var rateLimitCacheProvider = new LeakyBucketRateLimiter( redisRateLimiterSettings.RateLimitRedisCacheConnectionString, onException: (ex) => { //_logger.LogError("Error connecting to Redis") }, circuitBreaker: new DefaultCircuitBreaker(redisRateLimiterSettings.FaultThreshholdPerWindowDuration, redisRateLimiterSettings.FaultWindowDurationInMilliseconds, redisRateLimiterSettings.CircuitOpenIntervalInSecs, onCircuitException: (ex) => { //_logger.LogError("Rate limiting circuit error") }, onCircuitOpened: () => { //_logger.LogWarning("Rate limiting circuit opened") }, onCircuitClosed: () => { //logger.LogWarning("Rate limiting circuit closed") }), countThrottledRequests: true ); #endregion var configSettings = new DomainElasticSearchLoggingOptions() { Enabled = true, Url = "http://localhost:9200", Application = "Public Adapter", IndexFormat = "quotaaudits-{0:yyyy.MM.dd}" }; var loggerConfig = new LoggerConfiguration() .MinimumLevel.Information() .Enrich.FromLogContext(); // if enabled in appsettings.json, logs will be shipped to Elasticsearch in a standard json format if (configSettings.Enabled) { loggerConfig.WriteTo.DomainElasticsearch(configSettings); } var auditLogger = loggerConfig.CreateLogger(); var policyProvider = new ClientQuotaPolicyProvider(); filters.Add(new RateLimitingFilter( new RateLimiter(rateLimitCacheProvider, new RateLimitingPolicyManager(policyProvider)), filters, onPostLimit: async(request, policy, result, actionContext) => { var operationClass = CallClassification.RouteTemplateToClassMap[request.RouteTemplate]; var cost = CallClassification.CostPerClass[operationClass]; var clientId = result.CacheKey.RequestId; // sns publish if (result.State == ResultState.Success) { // sns publish auditLogger.Information( "Result {Result}: Request success for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost}", "Success", clientId, request.Path, request.RouteTemplate, operationClass, cost); } else if (result.State == ResultState.Throttled) { auditLogger.Information( "Result {Result}: throttled for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost} by violating policy {ViolatedPolicy}", "Throttled", clientId, request.Path, request.RouteTemplate, operationClass, cost, $"{policy.Name}:{result.CacheKey.AllowedConsumptionRate}"); } else if (result.State == ResultState.ThrottledButCompensationFailed) { auditLogger.Information( "Result {Result}: throttled but failed to compensate for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost} by violating policy {ViolatedPolicy}", result.State, clientId, request.Path, request.RouteTemplate, operationClass, cost, $"{policy.Name}:{result.CacheKey.AllowedConsumptionRate}"); } else if (result.State == ResultState.LimitApplicationFailed) { auditLogger.Information( "Result {Result}: Free pass for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost}", "FreePass", clientId, request.Path, request.RouteTemplate, operationClass, cost); } else if (result.State == ResultState.NotApplicable) { auditLogger.Information( "Result {Result}: Not Applicable for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost}", ResultState.NotApplicable, clientId, request.Path, request.RouteTemplate, operationClass, cost); } return(Decision.OK); }, onPostLimitRevert: async(request, policy, result, actionContext) => { var operationClass = CallClassification.RouteTemplateToClassMap[request.RouteTemplate]; if (result.State == ResultState.Success || result.State == ResultState.Throttled) { auditLogger.Information( "Result {Result}: Limit Cost Reverted for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost}", "SuccessCostReverted", result.CacheKey.RequestId, request.Path, request.RouteTemplate, operationClass, -policy.CostPerCall); } else if (result.State == ResultState.LimitApplicationFailed) { auditLogger.Information( "Result {Result}: Limit Cost Reverting failed for client {ClientId} and endpoint {Endpoint} with route {RouteTemplate} which is Class {Class} with Cost {Cost}", "SuccessCostRevertingFailed", result.CacheKey.RequestId, request.Path, request.RouteTemplate, operationClass, -policy.CostPerCall); } }, postOperationDecisionFuncAsync: async(request, policy, result, actionExecutedContext) => { if (actionExecutedContext.Exception != null || (int)actionExecutedContext.Response.StatusCode >= 400) { return(Decision.REVERTSUCCESSCOST); } return(Decision.OK); }, //getPolicyFuncAsync: policyProvider.GetPolicyAsync, simulationMode: false)); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Adds services required for using options. services.AddOptions(); #region Setting policies explicitly using code var rateLimitingPolicyParametersProvider = new SampleRateLimitingClientPolicyProvider(); var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager(rateLimitingPolicyParametersProvider) .AddPathToWhiteList("/api/unlimited") .AddPoliciesForAllEndpoints(new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(1000, RateLimitUnit.PerMinute) }, allowAttributeOverride: true, name: "StaticPolicy_2") .AddEndpointPolicy("/api/globallylimited/{id}", "GET", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(5, RateLimitUnit.PerMinute), new AllowedConsumptionRate(8, RateLimitUnit.PerHour) }, true, "StaticPolicy_0") .AddEndpointPolicy("/api/globallylimited/{id}/sub/{subid}", RateLimitPolicy.AllHttpMethods, new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerMinute) }, true, "StaticPolicy_1"); #endregion #region Setting Policies Using Configuration Options // var rateLimitingOptions = new RateLimitingOptions(); // Configuration.GetSection(nameof(RateLimitingOptions)).Bind(rateLimitingOptions); // var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager( // new SampleRateLimitingClientPolicyProvider()) // .AddPoliciesForAllEndpoints(new List<AllowedCallRate>() {new AllowedCallRate(180, RateLimitUnit.PerMinute)},name:"ClientPolicy") // .AddPathsToWhiteList(rateLimitingOptions.RateLimitingWhiteListedPaths) // .AddRequestKeysToWhiteList(rateLimitingOptions.RateLimitingWhiteListedRequestKeys); #endregion #region Setting up the Redis rate limiter var redisRateLimiterSettings = new RedisRateLimiterSettings(); Configuration.GetSection(nameof(RedisRateLimiterSettings)).Bind(redisRateLimiterSettings); var rateLimitCacheProvider = new SlidingTimeWindowRateLimiter( redisRateLimiterSettings.RateLimitRedisCacheConnectionString, (exp) => _logger.LogError("Error in rate limiting", exp), onThrottled: (rateLimitingResult) => { _logger.LogInformation( "Request throttled for client {ClientId} and endpoint {Endpoint}", rateLimitingResult.CacheKey.RequestId, rateLimitingResult.CacheKey.RouteTemplate); }, circuitBreaker: new DefaultCircuitBreaker(redisRateLimiterSettings.FaultThreshholdPerWindowDuration, redisRateLimiterSettings.FaultWindowDurationInMilliseconds, redisRateLimiterSettings.CircuitOpenIntervalInSecs, onCircuitOpened: () => _logger.LogWarning("Rate limiting circuit opened"), onCircuitClosed: () => _logger.LogWarning("Rate limiting circuit closed"))); #endregion // Add framework services services.AddMvc(options => { #region Adding the RateLimitingFilter options.Filters.Add(new RateLimitingFilter( new RateLimiter(rateLimitCacheProvider, globalRateLimitingClientPolicyManager))); #endregion #region Multi level rate limiting - Multiple action filters based on separate Policy Providers providing separate policies //options.Filters.Add(new RateLimitingFilter( // new RateLimiter(rateLimitCacheProvider, new SampleRateLimitingUserPolicyProvider()))); //options.Filters.Add(new RateLimitingFilter( // new RateLimiter(rateLimitCacheProvider, new SampleRateLimitingOrganizationPolicyProvider()))); #endregion }); }