繁体   English   中英

限制每个 API 端点的并发

[英]Limit concurrency per API endpoint

如何限制 .net 核心中每个 API 端点的并发性。 我需要一个端点来限制我的微服务中的单个请求。

尽管我对支持您的单例 Web 服务特定用例的实际细节感到好奇。 我会回答你的问题。

想到的最优雅的解决方案是使用自定义中间件: 编写自定义 ASP.NET Core 中间件

它的代码可能是这样的:

// Example middleware
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;

namespace YourNamespaceHere
{
    public class MySingleRequestLimitingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IMemoryCache _cache;
        private readonly string FlagCacheKey = "RequestAvailability";
        private readonly string _statusLocked = "locked";
        private readonly string _statusAvailable = "available";

        public MySingleRequestLimitingMiddleware(RequestDelegate next,
            IMemoryCache cache)
        {
            _next = next;
            _cache = cache;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            string cacheEntry;

            // Set cache options.
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                // Keep in cache for 2 minutes.
                .SetAbsoluteExpiration(DateTimeOffset.Now.AddMinutes(2));

            // Look for the cache key.
            if (!_cache.TryGetValue(FlagCacheKey, out cacheEntry))
            {
                // Key not in cache, so set lock.
                cacheEntry = _statusAvailable;

                // Save data in cache.
                _cache.Set(FlagCacheKey, cacheEntry, cacheEntryOptions);
            }

            if (cacheEntry.Equals(_statusLocked, StringComparison.OrdinalIgnoreCase))
            {
                httpContext.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
                httpContext.Response.ContentType = "text/plain";

                await httpContext.Response.WriteAsync("Maximum API calls admitted: 1.");

                return;
            }

            /* ***BEWARE: You are entering the request the next line
             * will lock up the call until this flag is changed.
             * You must set the flag in your controller or somewhere else every single
             * request no fail or your call will be left in an unusable state.
             * The only thing that fixes the state if you fail to change state
             * is restarting the service or waiting 2 minutes. */
            _cache.Set(FlagCacheKey, _statusLocked, cacheEntryOptions);

            await _next.Invoke(httpContext);
        }
    }
}

// In your Startup.cs
namespace YourNamespaceHere
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMemoryCache(); // Add this line to yours somewhere
        }

        public void Configure(IApplicationBuilder app)
        {
            // Put it at very top because you don't need to run anything
            // else if the limit of 1 request has already been reached.
            app.UseWhen(context => context.Request.Path.StartsWithSegments("/somepathtoyourcallhere"), appBuilder =>
            {
                appBuilder.UseMySingleRequestLimitingMiddleware();
            });

            // The rest of your code
        }
    }
}

// Example Controller
// In the controller containing your singleton request call
// ***NOTE you MUST set the flag even if there is an error!!!
public class SomeController : Controller
{
    private IMemoryCache _cache;

    public SomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

    [HttpPost]
    public IActionResult YourSingleServiceRequestActionNameHere(SomeMode model)
    {
        // Some code here...
        _cache.Set(FlagCacheKey, "available", new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(DateTimeOffset.Now.AddMinutes(2)));
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Just in case you didn't set that flag already lets make sure.
            _cache.Set(FlagCacheKey, "available", new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(DateTimeOffset.Now.AddMinutes(2)));
        }

        base.Dispose(disposing);
    }
}

也可以考虑使用IP或者Client limit rating中间件比如: AspNetCoreRateLimit

由于我们不知道请求何时完成,因此我们将缓存过期时间设置为 2 分钟,因此如果您的代码中的标志无法更新,服务将在不超过 2 分钟的时间内无法使用。 您的通话是否需要 2 分钟才能完成? 如果是这样,那么 Web 请求可能会在请求完成之前超时。

如果完成时间超过 2 分钟,请考虑 Web 服务调用是否是正确的解决方案。

希望有帮助。

快乐编码!!!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM