public void ConfigureServices(IServiceCollection services) { services.AddStackPolicy(options => { //最大并发请求数 options.MaxConcurrentRequests = 2; //请求队列长度限制 options.RequestQueueLimit = 1; }); services.AddControllers(); }
通过上面的配置,我们便可以对我们的应用程序执行出相应的策略.下面再来看看他是怎么实现的呢
public static IServiceCollection AddStackPolicy(this IServiceCollection services, Action<QueuePolicyOptions> configure) { services.Configure(configure); services.AddSingleton<IQueuePolicy, StackPolicy>(); return services; }
可以看到这次是通过StackPolicy类做的策略.来一起来看看主要的方法
/// <summary> /// 构造方法(初始化参数) /// </summary> /// <param></param> public StackPolicy(IOptions<QueuePolicyOptions> options) { //栈分配 _buffer = new List<ResettableBooleanCompletionSource>(); //队列大小 _maxQueueCapacity = options.Value.RequestQueueLimit; //最大并发请求数 _maxConcurrentRequests = options.Value.MaxConcurrentRequests; //剩余可用空间 _freeServerSpots = options.Value.MaxConcurrentRequests; }
当我们通过中间件请求调用,_queuePolicy.TryEnterAsync()时,首先会判断我们是否还有访问请求次数,如果_freeServerSpots>0,那么则直接给我们返回true,让中间件直接去执行下一步,如果当前队列=我们设置的队列大小的话,那我们需要取消先前请求;每次取消都是先取消之前的保留后面的请求;
public ValueTask<bool> TryEnterAsync() { lock (_bufferLock) { if (_freeServerSpots > 0) { _freeServerSpots--; return _trueTask; } // 如果队列满了,取消先前的请求 if (_queueLength == _maxQueueCapacity) { _hasReachedCapacity = true; _buffer[_head].Complete(false); _queueLength--; } var tcs = _cachedResettableTCS ??= new ResettableBooleanCompletionSource(this); _cachedResettableTCS = null; if (_hasReachedCapacity || _queueLength < _buffer.Count) { _buffer[_head] = tcs; } else { _buffer.Add(tcs); } _queueLength++; // increment _head for next time _head++; if (_head == _maxQueueCapacity) { _head = 0; } return tcs.GetValueTask(); } }
当我们请求后调用_queuePolicy.OnExit();出栈,再将请求长度递减
public void OnExit() { lock (_bufferLock) { if (_queueLength == 0) { _freeServerSpots++; if (_freeServerSpots > _maxConcurrentRequests) { _freeServerSpots--; throw new InvalidOperationException("OnExit must only be called once per successful call to TryEnterAsync"); } return; } // step backwards and launch a new task if (_head == 0) { _head = _maxQueueCapacity - 1; } else { _head--; } //退出,出栈 _buffer[_head].Complete(true); _queueLength--; } }
总结
基于栈结构的特点,在实际应用中,通常只会对栈执行以下两种操作:
向栈中添加元素,此过程被称为"进栈"(入栈或压栈);
从栈中提取出指定元素,此过程被称为"出栈"(或弹栈);
队列存储结构的实现有以下两种方式:
顺序队列:在顺序表的基础上实现的队列结构;
链队列:在链表的基础上实现的队列结构;