/// <summary> /// request can open rateLimit /// </summary> /// <param name="context"></param> /// <param name="methodCacheName"></param> /// <returns></returns> private async Task <(bool, EnableRateLimitAttribute)> RequestNeedRateLimitAsync(HttpContext context, string methodCacheName) { EnableRateLimitAttribute enableRateLimitAttribute = null; bool specialRuleResult = _specialRule != null && _specialRule.MethodList != null && _specialRule.MethodList.Any() && _specialRule.MethodList.Contains(methodCacheName); if (!specialRuleResult) { //wheather need RateLimit var endpoint = GetEndpoint(context); if (endpoint != null) { enableRateLimitAttribute = endpoint.Metadata.GetMetadata <EnableRateLimitAttribute>(); if (enableRateLimitAttribute == null) { await _next(context); return(false, enableRateLimitAttribute); } var disableRateLimitAttribute = endpoint.Metadata.GetMetadata <DisableRateLimitAttribute>(); if (disableRateLimitAttribute != null) { await _next(context); return(false, enableRateLimitAttribute); } } else { await _next(context); return(false, enableRateLimitAttribute); } } return(true, enableRateLimitAttribute); }
/// <summary> /// Asynchronous processing of Middleware /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task Invoke(HttpContext context) { #region check whether need RateLimit and local Attribute is prority then other rules string methodCacheName = ContextMethodNameHandler(context); var requestNeedRateLimt = await RequestNeedRateLimitAsync(context, methodCacheName); if (!requestNeedRateLimt.Item1) { return; } EnableRateLimitAttribute enableRateLimitAttribute = requestNeedRateLimt.Item2; #endregion check whether need RateLimit and local Attribute is prority then other rules //local method attribute var getMethodAttrRateLimitData = GetRateLimitTypeAttributeData(context); // context.Items.Add("PollyRequire", _specialRule != null ? _specialRule.EnablePolly : false); await DoOnBeforeCheck(context, _algorithm).ConfigureAwait(false); AlgorithmCheckResult checkResult;//= await _algorithm.CheckAsync(context); #region Polly bool enablePolly = enableRateLimitAttribute == null ? (_specialRule != null ? _specialRule.EnablePolly : false) : enableRateLimitAttribute.EnablePolly; if (enablePolly) { checkResult = await PollyRateLimitAdvancedCircuitBreakerAsync(context.Request.Path.Value, enableRateLimitAttribute) .ExecuteAsync(async() => { return(await _algorithm.CheckAsync(context, getMethodAttrRateLimitData)); }); } else { checkResult = await _algorithm.CheckAsync(context, getMethodAttrRateLimitData); } #endregion Polly await DoOnAfterCheck(context, checkResult).ConfigureAwait(false); if (checkResult.IsLimit) { if (checkResult.RuleCheckResults != null && checkResult.RuleCheckResults.Any()) { var checkException = checkResult.RuleCheckResults.FirstOrDefault(x => x.RateLimitExceptionThrow == true); if (checkException != null) { throw new RateLimitException(methodCacheName); } } await DoOnTriggered(context, checkResult).ConfigureAwait(false); context.Response.StatusCode = _error.HttpStatusCode; await SetHeaders(context, checkResult).ConfigureAwait(false); await ReponseWithTooManyRequests(context, checkResult).ConfigureAwait(false); } else { await DoOnBreforUntriggeredDoNext(context, checkResult).ConfigureAwait(false); await DoLeakyBucketWait(checkResult).ConfigureAwait(false); //Debug.WriteLine("R-Count" + checkResult.RuleCheckResults.First().Count + " " + DateTimeOffset.Now.ToString("mm:ss.fff")); await _next(context); await DoOnAfterUntriggeredDoNext(context, checkResult).ConfigureAwait(false); } }
/// <summary> /// rateLimit exception /// </summary> /// <returns></returns> private static AsyncPolicy <AlgorithmCheckResult> PollyRateLimitAdvancedCircuitBreakerAsync(string policyKey, EnableRateLimitAttribute enableRateLimitAttribute) { var getResult = _getPollyObj.TryGetValue(policyKey, out AsyncPolicy <AlgorithmCheckResult> pollyObj); if (getResult) { return(pollyObj); } int retryCount = enableRateLimitAttribute == null ? 1 : enableRateLimitAttribute.RetryCount; double failureThreshold = enableRateLimitAttribute == null ? 0.75 : enableRateLimitAttribute.FailureThreshold; TimeSpan samplingDuration = enableRateLimitAttribute == null?TimeSpan.FromSeconds(10) : enableRateLimitAttribute.SamplingDuration; int minimumThroughput = enableRateLimitAttribute == null ? 100 : enableRateLimitAttribute.MinimumThroughput; TimeSpan durationOfBreak = enableRateLimitAttribute == null?TimeSpan.FromSeconds(10) : enableRateLimitAttribute.DurationOfBreak; var breakPolicy = Policy <AlgorithmCheckResult> .Handle <RateLimitException>().AdvancedCircuitBreakerAsync( failureThreshold: failureThreshold, samplingDuration: samplingDuration, minimumThroughput: minimumThroughput, durationOfBreak: durationOfBreak, onBreak: (r, t) => { Console.WriteLine("onbreak"); }, onReset: () => { Console.WriteLine("onReset"); }, onHalfOpen: () => { Console.WriteLine("onHalfOpen"); } ); var retry = Policy <AlgorithmCheckResult> .Handle <RateLimitException>().WaitAndRetryAsync(retryCount, i => TimeSpan.FromMilliseconds(100 * i)); var message = new AlgorithmCheckResult(new List <RuleCheckResult>() { new RuleCheckResult() { IsLimit = true } }) { }; var fallback = Policy <AlgorithmCheckResult> .Handle <BrokenCircuitException>().FallbackAsync(message); var fallbackBreak = Policy.WrapAsync(fallback, retry, breakPolicy).WithPolicyKey(policyKey); _getPollyObj.TryAdd(policyKey, fallbackBreak); return(fallbackBreak); }