Concurrency limiters: What are they for?
Programming ideas known as concurrency limiters help control how many users can access a resource at once. It puts in place a rate limiter to restrict the amount of requests that may be processed at once. It protects applications and APIs from misuse and malevolent assaults.
Concurrency limiters play a crucial role in programming, especially in web development, as they ensure resource efficiency and protect against malicious assaults.
What has altered with.NET 8?
The ConcurrencyLimiterMiddleware and related classes and methods were marked as obsolete in.NET 8.
Use the more advanced and capable.NET 7 middleware (RateLimiterApplicationBuilderExtensions.UseRateLimiter) if you require rate-limiting features. A concurrency limiter and many additional ways are included in the rate-limiting API for.NET 7 and later versions, which you can utilize in your application.
For example, to add rate limiter to a short-circuit route, use this line of code.
Include the code in Program.cs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | app<span class="token punctuation">.</span><span class="token function">UseRateLimiter</span><span class="token punctuation">(</span><span class="token keyword keyword-new">new</span> <span class="token constructor-invocation class-name">RateLimiterOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">AddConcurrencyLimiter</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-market"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> options<span class="token punctuation">.</span>PermitLimit <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueLimit <span class="token operator">=</span> <span class="token number">15</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueProcessingOrder <span class="token operator">=</span> QueueProcessingOrder<span class="token punctuation">.</span>OldestFirst<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/market"</span><span class="token punctuation">,</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword keyword-await">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span> context<span class="token punctuation">.</span>Response<span class="token punctuation">.</span><span class="token function">Redirect</span><span class="token punctuation">(</span><span class="token string">"https://contoso.com/market?redir="</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">RequireRateLimiting</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-market"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">UseRateLimiter</span><span class="token punctuation">(</span><span class="token keyword keyword-new">new</span> <span class="token constructor-invocation class-name">RateLimiterOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">AddConcurrencyLimiter</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-crm"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> options<span class="token punctuation">.</span>PermitLimit <span class="token operator">=</span> <span class="token number">6</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueLimit <span class="token operator">=</span> <span class="token number">35</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueProcessingOrder <span class="token operator">=</span> QueueProcessingOrder<span class="token punctuation">.</span>NewestFirst<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/crm"</span><span class="token punctuation">,</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword keyword-await">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> context<span class="token punctuation">.</span>Response<span class="token punctuation">.</span><span class="token function">Redirect</span><span class="token punctuation">(</span><span class="token string">"https://contoso.com/crm?redir="</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">RequireRateLimiting</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-crm"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">UseRateLimiter</span><span class="token punctuation">(</span><span class="token keyword keyword-new">new</span> <span class="token constructor-invocation class-name">RateLimiterOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">AddConcurrencyLimiter</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-sales"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>options<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> options<span class="token punctuation">.</span>PermitLimit <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueLimit <span class="token operator">=</span> <span class="token number">25</span><span class="token punctuation">;</span> options<span class="token punctuation">.</span>QueueProcessingOrder <span class="token operator">=</span> QueueProcessingOrder<span class="token punctuation">.</span>OldestFirst<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/sales"</span><span class="token punctuation">,</span> <span class="token keyword keyword-async">async</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword keyword-await">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span><span class="token number">300</span><span class="token punctuation">)</span><span class="token punctuation">;</span> context<span class="token punctuation">.</span>Response<span class="token punctuation">.</span><span class="token function">Redirect</span><span class="token punctuation">(</span><span class="token string">"https://contoso.com/sales?redir="</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">RequireRateLimiting</span><span class="token punctuation">(</span><span class="token string">"only-one-at-a-time-sales"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
To begin using this code, please ensure you complete the necessary configuration steps first.
Add app.UseRateLimiter() after builder.Build().
To add rate-limiting middleware to the application pipeline, use the app.useRateLimiter method.
The RateLimiterOptions argument is used to configure the rate limiter. AddConcurrencyLimiter: This adds a concurrency limiter with the policy name “only-one-at-a-time-market/crm/sales” to the rate limiter. While the QueueLimit property indicates the maximum number of queued requests, the PermitLimit property indicates the maximum number of concurrent requests permitted. How requests from the queue are handled is determined by the QueueProcessingOrder option.
The application.The delegate that is created by the MapGet method receives the /market, /crm e /sales route and waits [x] milliseconds before redirecting the request to “https://contoso.com/[key]?redir= With the policy “name only-one-at-a-time-[key],” rate limiting is enforced for the ‘/[key]’ route using the RequireRateLimiting method.
A new feature in.NET 7 is the rate-limiting API, which limits the number of times a resource may be visited, protecting it against overloading.
.NET 8 includes four rate-limiting algorithms.
1. Concurrency limit: This sets a restriction on how many requests can use a resource at once. Only ten requests can access a resource at once if your limit is ten; the eleventh request will be rejected. The number of completed requests increases to one; with the completion of another request, the number rises to two, and so forth.
public class ConcurrencyLimiterMiddleware
{
private readonly RequestDelegate _next;
private static SemaphoreSlim _semaphore = new SemaphoreSlim(10); // 10 concurrent requests
public ConcurrencyLimiterMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (_semaphore.CurrentCount == 0)
{
context.Response.StatusCode = 429; // Too Many Requests
return;
}
await _semaphore.WaitAsync();
try
{
await _next(context);
}
finally
{
_semaphore.Release();
}
}
}
2. Token bucket limit: This limits the amount of requests based on a predetermined amount of permitted requests. Imagine a bucket overflowing with tokens. It takes a token upon receiving a request and keeps it forever. After a set amount of time, someone refills the bucket with a predetermined number of tokens, never adding more than it can hold. The request is not granted access to the resource when it is received and the bucket is empty.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword keyword-public">public</span> <span class="token keyword keyword-class">class</span> <span class="token class-name">TokenBucketLimiterMiddleware</span> <span class="token punctuation">{</span> <span class="token keyword keyword-private">private</span> <span class="token keyword keyword-readonly">readonly</span> <span class="token class-name">RequestDelegate</span> _next<span class="token punctuation">;</span> <span class="token keyword keyword-private">private</span> <span class="token keyword keyword-static">static</span> <span class="token class-name"><span class="token keyword keyword-int">int</span></span> _tokenCount <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span> <span class="token comment">// Total tokens</span> <span class="token keyword keyword-private">private</span> <span class="token keyword keyword-static">static</span> <span class="token class-name"><span class="token keyword keyword-object">object</span></span> _lock <span class="token operator">=</span> <span class="token keyword keyword-new">new</span> <span class="token constructor-invocation class-name"><span class="token keyword keyword-object">object</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword keyword-public">public</span> <span class="token function">TokenBucketLimiterMiddleware</span><span class="token punctuation">(</span><span class="token class-name">RequestDelegate</span> next<span class="token punctuation">)</span> <span class="token punctuation">{</span> _next <span class="token operator">=</span> next<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword keyword-public">public</span> <span class="token keyword keyword-async">async</span> <span class="token return-type class-name">Task</span> <span class="token function">InvokeAsync</span><span class="token punctuation">(</span><span class="token class-name">HttpContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword keyword-lock">lock</span> <span class="token punctuation">(</span>_lock<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword keyword-if">if</span> <span class="token punctuation">(</span>_tokenCount <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> context<span class="token punctuation">.</span>Response<span class="token punctuation">.</span>StatusCode <span class="token operator">=</span> <span class="token number">429</span><span class="token punctuation">;</span> <span class="token comment">// Too Many Requests</span> <span class="token keyword keyword-return">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> _tokenCount<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token comment">// Take a token</span> <span class="token punctuation">}</span> <span class="token comment">// Periodically refill the bucket</span> <span class="token comment">// (This would typically be done with a background task)</span> <span class="token keyword keyword-await">await</span> <span class="token function">_next</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
3. Fixed window limit: Makes use of the concept of a window, which will also be used in the following method. The window is the period before we go on to the next window where our limit is applied. Moving to the next window in the fixed window case entails resetting the limit to its beginning point.
4. Sliding window limit: This approach is similar to the fixed window algorithm, but instead of resetting the limit to its initial position, the window slides forward by a defined length of time.
Could you kindly show me how to use this in Program.cs? Please select just one limiter. 😊
Observations: Place these middleware examples before any middleware that processes requests, such as app.Use() or app.Mvc().UseEndpoints().
Verify that the middleware has been registered in the correct order. Middleware in ASP.NET Core is run in the order that it is added to the pipeline.
For more complex applications, especially those involving distributed systems, think about utilizing well-known libraries or the rate limiting and concurrency control capabilities that are already included in ASP.NET Core. These are excellent samples that would need to be modified for usage in actual production.
It is important to keep in mind that every middleware has its own state and behavior, which might or might not be shared by several applications. For distributed apps, you might need a distributed cache or something similar to share state.
ASP.NET 8 Hosting Recommendation
ASP.NET is a powerful platform for creating web applications and services. You must be comfortable with JavaScript, HTML, CSS, and C# before developing a web application in ASP.NET. On the market, there are thousands of web hosting companies providing ASP.NET Hosting. But, only very few web hosting companies could provide high quality ASP.NET hosting solution.
ASP.NET is the best development language in Windows platform, which is released by Microsoft and widely used to build all types of dynamic Web sites and XML Web services. With this article, we’re going to help you to find the best ASP.NET Hosting solution in Europe based on reliability, features, price, performance and technical support. After we reviewed about 30+ ASP.NET hosting providers in Europe, our Best ASP.NET Hosting Award in Europe goes to HostForLIFE.eu, one of the fastest growing private companies and one of the most reliable hosting providers in Europe.