.net C# 流量限制令牌桶算法工具类
流量限制令牌桶算法工具类
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Common
{
/// <summary>
/// 令牌桶算法工具类
/// </summary>
public class TokenBucket
{
/// <summary>
/// 令牌桶队列
/// </summary>
private readonly Queue<int> _tokens = new Queue<int>();
/// <summary>
/// 令牌桶容量(最大令牌数)
/// </summary>
private readonly int _bucketSize;
/// <summary>
/// 初始化令牌桶集合间隔(秒)
/// </summary>
public int BucketSize => _bucketSize;
/// <summary>
/// 初始化令牌桶集合间隔(秒)
/// </summary>
private readonly int _tokensPerSecond;
/// <summary>
/// 时间间隔监听器
/// </summary>
private readonly Stopwatch _stopwatch = new Stopwatch();
/// <summary>
/// 令牌桶
/// </summary>
/// <param name="bucketSize">令牌桶容量(默认:512KB)</param>
/// <param name="tokensPerSecond">初始化令牌桶集合间隔(默认:1秒)</param>
public TokenBucket(int bucketSize = 512 * 1024, int tokensPerSecond = 1)
{
_bucketSize = bucketSize;
_tokensPerSecond = tokensPerSecond;
}
/// <summary>
/// 请求口令
/// </summary>
/// <param name="count">请求分配令牌个数</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>已分配口令个数</returns>
public async Task<int> GetToken(int count, CancellationToken cancellationToken = default)
{
//启动监听
if (!_stopwatch.IsRunning)
{
_stopwatch.Start();
}
//重新监听
else if (_stopwatch.ElapsedMilliseconds >= _tokensPerSecond * 1000)
{
_stopwatch.Reset();
}
//初始化令牌桶
while (_tokens.Count < _bucketSize && _stopwatch.ElapsedMilliseconds < _tokensPerSecond * 1000)
{
_tokens.Enqueue(1);
}
if (_tokens.Count > 0)
{
//分配口令
for (int i = 0; i < count; i++)
{
//如果无可用口令,等待下一轮初始化口令
if (_tokens.Count == 0)
{
_stopwatch.Stop();
await Task.Delay(_tokensPerSecond * 1000 - (int)_stopwatch.ElapsedMilliseconds, cancellationToken);
//返回已分配口令个数
return i;
}
_tokens.Dequeue();
}
return count;
}
else
{
return 0;
}
}
/// <summary>
/// 使用令牌
/// </summary>
/// <param name="count">使用的令牌数,如果大于最大容量,则循环分配令牌</param>
/// <param name="func">令牌分配委托,参数1:待分配索引,参数2:已分配口令个数</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task UseToken(int count, Func<int, int, Task> func, CancellationToken cancellationToken = default)
{
int i = 0;
int t = count;
while (t > 0)
{
int j;
if ((j = await GetToken(t, cancellationToken)) > 0)
{
await func(i, j);
t -= j;
i += j;
}
}
}
}
}
用法:
await tokenBucket.UseToken(buffer.Length, new Func<int, int, Task>(async (i, j) =>
{
await wirteStream.WriteAsync(bytes.Slice(i, j), cancellationToken);
}), cancellationToken);

浙公网安备 33010602011771号