gpt4 book ai didi

c# - 使用 async/await 时处理节流/速率限制(429 错误)

转载 作者:行者123 更新时间:2023-12-03 12:45:07 25 4
gpt4 key购买 nike

我的项目中有很多地方会调用以下异步代码:

public async Task<HttpResponseMessage> MakeRequestAsync(HttpRequestMessage request)
{
var client = new HttpClient();
return await client.SendAsync(request).ConfigureAwait(false);
}

如何调用上述方法的示例:

 var tasks = items.Select(async i =>
{
var response = await MakeRequestAsync(i.Url);
//do something with response
});

我访问的 ZenDesk API 允许每分钟大约 200 个请求,之后我收到 429 错误。如果遇到 429 错误,我需要执行某种 Thread.sleep,但是使用 async/await,并行线程中可能有很多请求等待处理,我不确定如何让它们全部 sleep 持续 5 秒左右,然后再次恢复。

解决这个问题的正确方法是什么?我想听到快速解决方案以及设计良好的解决方案。

最佳答案

我不认为这是最近标记的重复项。另一个 SO 发布者不需要基于时间的滑动窗口(或基于时间的节流),那里的答案没有涵盖这种情况。这仅在您想对传出请求设置硬限制时才有效。

无论如何,一个准快速的解决方案是在 MakeRequestAsync 方法中进行限制。像这样:

public async Task<HttpResponseMessage> MakeRequestAsync(HttpRequestMessage request)
{
//Wait while the limit has been reached.
while(!_throttlingHelper.RequestAllowed)
{
await Task.Delay(1000);
}

var client = new HttpClient();

_throttlingHelper.StartRequest();
var result = await client.SendAsync(request).ConfigureAwait(false);
_throttlingHelper.EndRequest();

return result;
}

ThrottlingHelper 类只是我现在制作的东西,因此您可能需要对其进行一些调试(阅读 - 可能无法开箱即用)。它试图成为一个时间戳滑动窗口。

public class ThrottlingHelper : IDisposable
{
//Holds time stamps for all started requests
private readonly List<long> _requestsTx;
private readonly ReaderWriterLockSlim _lock;

private readonly int _maxLimit;
private TimeSpan _interval;

public ThrottlingHelper(int maxLimit, TimeSpan interval)
{
_requestsTx = new List<long>();
_maxLimit = maxLimit;
_interval = interval;
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}

public bool RequestAllowed
{
get
{
_lock.EnterReadLock();
try
{
var nowTx = DateTime.Now.Ticks;
return _requestsTx.Count(tx => nowTx - tx < _interval.Ticks) < _maxLimit;
}
finally
{
_lock.ExitReadLock();
}
}
}

public void StartRequest()
{
_lock.EnterWriteLock();
try
{
_requestsTx.Add(DateTime.Now.Ticks);
}
finally
{
_lock.ExitWriteLock();
}
}

public void EndRequest()
{
_lock.EnterWriteLock();
try
{
var nowTx = DateTime.Now.Ticks;
_requestsTx.RemoveAll(tx => nowTx - tx >= _interval.Ticks);
}
finally
{
_lock.ExitWriteLock();
}
}

public void Dispose()
{
_lock.Dispose();
}
}

您可以将它用作发出请求的类中的成员,并像这样实例化它:

_throttlingHelper = new ThrottlingHelper(200, TimeSpan.FromMinutes(1));

不要忘记在用完后丢弃它。

一些关于 ThrottlingHelper 的文档:

  1. 构造函数参数是您希望在特定时间间隔内能够执行的最大请求,而时间间隔本身就是一个时间跨度。因此,200 和 1 分钟意味着您希望每分钟不超过 200 个请求。
  2. 属性 RequestAllowed 让您知道您是否能够使用当前的限制设置发出请求。
  3. 方法 StartRequestEndRequest 使用当前日期/时间注册/取消注册请求。

编辑/陷阱

正如@PhilipABarnes 所指出的,EndRequest 可能会删除仍在进行中的请求。据我所知,这可能发生在两种情况下:

  1. 间隔很小,因此请求无法及时完成。
  2. 请求实际花费的时间超过了执行的时间间隔。

建议的解决方案涉及通过 GUID 或类似的方式将 EndRequest 调用与 StartRequest 调用实际匹配。

关于c# - 使用 async/await 时处理节流/速率限制(429 错误),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25028580/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com