实现代码如下builder.UseRateLimiting();,我们转到定义,得到如下代码,详细的实现逻辑在ClientRateLimitMiddleware方法里,继续转定义到这个方法,我把方法里用到的内容注释了下。
public static class RateLimitMiddlewareExtensions { public static IOcelotPipelineBuilder UseRateLimiting(this IOcelotPipelineBuilder builder) { return builder.UseMiddleware<ClientRateLimitMiddleware>(); } } public class ClientRateLimitMiddleware : OcelotMiddleware { private readonly OcelotRequestDelegate _next; private readonly IRateLimitCounterHandler _counterHandler; private readonly ClientRateLimitProcessor _processor; public ClientRateLimitMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, IRateLimitCounterHandler counterHandler) :base(loggerFactory.CreateLogger<ClientRateLimitMiddleware>()) { _next = next; _counterHandler = counterHandler; _processor = new ClientRateLimitProcessor(counterHandler); } //熟悉的Tnvoke方法,所有的逻辑都在此方法里。 public async Task Invoke(DownstreamContext context) { var options = context.DownstreamReRoute.RateLimitOptions; // 校验是否启用限流配置 if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting) {//未启用直接进入下一个中间件 Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}"); await _next.Invoke(context); return; } // 获取配置的校验客户端的方式 var identity = SetIdentity(context.HttpContext, options); // 校验是否为白名单 if (IsWhitelisted(identity, options)) {//白名单直接放行 Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting"); await _next.Invoke(context); return; } var rule = options.RateLimitRule; if (rule.Limit > 0) {//限流数是否大于0 // 获取当前客户端请求情况,这里需要注意_processor是从哪里注入的,后续重 var counter = _processor.ProcessRequest(identity, options); // 校验请求数是否大于限流数 if (counter.TotalRequests > rule.Limit) { //获取下次有效请求的时间,就是避免每次请求,都校验一次 var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule); // 写入日志 LogBlockedRequest(context.HttpContext, identity, counter, rule, context.DownstreamReRoute); var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture); // 抛出超出限流异常并把下次可请求时间写入header里。 await ReturnQuotaExceededResponse(context.HttpContext, options, retrystring); return; } } //如果启用了限流头部 if (!options.DisableRateLimitHeaders) { var headers = _processor.GetRateLimitHeaders(context.HttpContext, identity, options); context.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers); } //进入下一个中间件 await _next.Invoke(context); } public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option) { var clientId = "client"; if (httpContext.Request.Headers.Keys.Contains(option.ClientIdHeader)) { clientId = httpContext.Request.Headers[option.ClientIdHeader].First(); } return new ClientRequestIdentity( clientId, httpContext.Request.Path.ToString().ToLowerInvariant(), httpContext.Request.Method.ToLowerInvariant() ); } public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option) { if (option.ClientWhitelist.Contains(requestIdentity.ClientId)) { return true; } return false; } public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute) { Logger.LogInformation( $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}."); } public virtual Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter) { var message = string.IsNullOrEmpty(option.QuotaExceededMessage) ? $"API calls quota exceeded! maximum admitted {option.RateLimitRule.Limit} per {option.RateLimitRule.Period}." : option.QuotaExceededMessage; if (!option.DisableRateLimitHeaders) { httpContext.Response.Headers["Retry-After"] = retryAfter; } httpContext.Response.StatusCode = option.HttpStatusCode; return httpContext.Response.WriteAsync(message); } private Task SetRateLimitHeaders(object rateLimitHeaders) { var headers = (RateLimitHeaders)rateLimitHeaders; headers.Context.Response.Headers["X-Rate-Limit-Limit"] = headers.Limit; headers.Context.Response.Headers["X-Rate-Limit-Remaining"] = headers.Remaining; headers.Context.Response.Headers["X-Rate-Limit-Reset"] = headers.Reset; return Task.CompletedTask; } }【.NET Core项目实战-统一认证平台】第二章网关篇-重构Ocelot来满足需求 (2)
内容版权声明:除非注明,否则皆为本站原创文章。