Improve performance for QueuePolicy in ConcurrencyLimiter (#13947)

This commit is contained in:
Kahbazi 2019-11-02 00:58:02 +03:30 committed by Stephen Halter
parent ab5b5a4ab5
commit 61179f3da2
2 changed files with 30 additions and 17 deletions

View File

@ -10,31 +10,35 @@ namespace Microsoft.AspNetCore.ConcurrencyLimiter
{ {
internal class QueuePolicy : IQueuePolicy, IDisposable internal class QueuePolicy : IQueuePolicy, IDisposable
{ {
private readonly int _maxConcurrentRequests; private readonly int _maxTotalRequest;
private readonly int _requestQueueLimit;
private readonly SemaphoreSlim _serverSemaphore; private readonly SemaphoreSlim _serverSemaphore;
private object _totalRequestsLock = new object(); private object _totalRequestsLock = new object();
public int TotalRequests { get; private set; } public int TotalRequests { get; private set; }
public QueuePolicy(IOptions<QueuePolicyOptions> options) public QueuePolicy(IOptions<QueuePolicyOptions> options)
{ {
_maxConcurrentRequests = options.Value.MaxConcurrentRequests; var queuePolicyOptions = options.Value;
if (_maxConcurrentRequests <= 0)
var maxConcurrentRequests = queuePolicyOptions.MaxConcurrentRequests;
if (maxConcurrentRequests <= 0)
{ {
throw new ArgumentException(nameof(_maxConcurrentRequests), "MaxConcurrentRequests must be a positive integer."); throw new ArgumentException(nameof(maxConcurrentRequests), "MaxConcurrentRequests must be a positive integer.");
} }
_requestQueueLimit = options.Value.RequestQueueLimit; var requestQueueLimit = queuePolicyOptions.RequestQueueLimit;
if (_requestQueueLimit < 0) if (requestQueueLimit < 0)
{ {
throw new ArgumentException(nameof(_requestQueueLimit), "The RequestQueueLimit cannot be a negative number."); throw new ArgumentException(nameof(requestQueueLimit), "The RequestQueueLimit cannot be a negative number.");
} }
_serverSemaphore = new SemaphoreSlim(_maxConcurrentRequests); _serverSemaphore = new SemaphoreSlim(maxConcurrentRequests);
_maxTotalRequest = maxConcurrentRequests + requestQueueLimit;
} }
public async ValueTask<bool> TryEnterAsync() public ValueTask<bool> TryEnterAsync()
{ {
// a return value of 'false' indicates that the request is rejected // a return value of 'false' indicates that the request is rejected
// a return value of 'true' indicates that the request may proceed // a return value of 'true' indicates that the request may proceed
@ -42,17 +46,21 @@ namespace Microsoft.AspNetCore.ConcurrencyLimiter
lock (_totalRequestsLock) lock (_totalRequestsLock)
{ {
if (TotalRequests >= _requestQueueLimit + _maxConcurrentRequests) if (TotalRequests >= _maxTotalRequest)
{ {
return false; return new ValueTask<bool>(false);
} }
TotalRequests++; TotalRequests++;
} }
await _serverSemaphore.WaitAsync(); Task task = _serverSemaphore.WaitAsync();
if (task.IsCompletedSuccessfully)
{
return new ValueTask<bool>(true);
}
return true; return SemaphoreAwaited(task);
} }
public void OnExit() public void OnExit()
@ -69,5 +77,12 @@ namespace Microsoft.AspNetCore.ConcurrencyLimiter
{ {
_serverSemaphore.Dispose(); _serverSemaphore.Dispose();
} }
private async ValueTask<bool> SemaphoreAwaited(Task task)
{
await task;
return true;
}
} }
} }

View File

@ -21,8 +21,6 @@ namespace Microsoft.AspNetCore.ConcurrencyLimiter
private readonly object _bufferLock = new Object(); private readonly object _bufferLock = new Object();
private readonly static ValueTask<bool> _trueTask = new ValueTask<bool>(true);
private int _freeServerSpots; private int _freeServerSpots;
public StackPolicy(IOptions<QueuePolicyOptions> options) public StackPolicy(IOptions<QueuePolicyOptions> options)
@ -40,7 +38,7 @@ namespace Microsoft.AspNetCore.ConcurrencyLimiter
if (_freeServerSpots > 0) if (_freeServerSpots > 0)
{ {
_freeServerSpots--; _freeServerSpots--;
return _trueTask; return new ValueTask<bool>(true);
} }
// if queue is full, cancel oldest request // if queue is full, cancel oldest request