处理 HTTP 429 错误的最佳方式
Best way to handle HTTP 429 errors
我正在点击一个 api 将 return 429,请求太多,如果您在五分钟内点击它超过 250 次则响应。计数每五分钟重置一次,所以我一直这样处理:
try
{
return request.GetResponse();
}
catch (Exception e)
{
if (e.Message.Contains("429"))
{
System.Threading.Thread.Sleep(5 * 60 * 1000);
return request.GetResponse();
}
else
{
throw new Exception(e.Message);
}
}
这是处理这种情况的正确方法吗?
您不应该对异常进行字符串解析 class。反过来,Exception classes 不应该将重要信息放入消息字段。
你对异常的整个捕获和重新抛出也是错误的。所以在这方面你应该阅读正确的异常处理。这是我 link 经常阅读的两篇文章:
http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx (死LINK)
http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
除非我弄错网络class,我想你应该catch WebExceptions only。我认为这就是您应该如何从此函数中获取 HTTP 错误代码,但我不太确定。
最好完全避免异常。所以我不得不问:为什么你甚至经常连续调用那个函数?批量检索没有合适的功能吗?该服务甚至打算像这样自动化吗?
这里要考虑的一件事是,如果您开始超过限制,您消耗的 api 可能会 "penalize"。我认为最好积极主动地限制您的 api 请求,这样您就永远不会收到 429 错误。
我们开始在我们的应用程序上遇到同样的事情(429 个错误)来自我们正在使用的 api。这个特定 api 的限制是每 60 秒 10 个。我们实现了一个 throttle()
函数,它利用嵌入了 date/time 的内存缓存。我们使用的 api 还以帐户为基础跟踪使用情况。你可能不需要那个。但这是我们用来限制我们的请求以确保我们始终低于限制的片段:
private void throttle()
{
var maxPerPeriod = 250;
//If you utilize multiple accounts, you can throttle per account. If not, don't use this:
var keyPrefix = "a_unique_id_for_the_basis_of_throttling";
var intervalPeriod = 300000;//5 minutes
var sleepInterval = 5000;//period to "sleep" before trying again (if the limits have been reached)
var recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
while (recentTransactions >= maxPerPeriod)
{
System.Threading.Thread.Sleep(sleepInterval);
recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
}
var key = keyPrefix + "_" + DateTime.Now.ToUniversalTime().ToString("yyyyMMddHHmm");
var existing = MemoryCache.Default.Where(x => x.Key.StartsWith(key));
if (existing != null && existing.Any())
{
var counter = 2;
var last = existing.OrderBy(x => x.Key).Last();
var pieces = last.Key.Split('_');
if (pieces.Count() > 2)
{
var lastCount = 0;
if (int.TryParse(pieces[2], out lastCount))
{
counter = lastCount + 1;
}
}
key = key + "_" + counter;
}
var policy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(intervalPeriod)
};
MemoryCache.Default.Set(key, 1, policy);
}
我们在代码中使用了这个 throttle()
函数,如下所示:
public override void DoAction()
{
throttle();
var url = ContentUri;
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("Authorization", "Bearer " + AccessToken);
request.Accept = "application/json";
WebResponse response = request.GetResponse();
var dataStream = new MemoryStream();
using (Stream responseStream = request.GetResponse().GetResponseStream())
{
//DO STUFF WITH THE DOWNLOADED DATA HERE...
}
dataStream.Close();
response.Close();
}
它本质上是在缓存中跟踪您的请求。如果已达到限制,它会暂停,直到经过足够的时间,您仍未达到限制。
我正在点击一个 api 将 return 429,请求太多,如果您在五分钟内点击它超过 250 次则响应。计数每五分钟重置一次,所以我一直这样处理:
try
{
return request.GetResponse();
}
catch (Exception e)
{
if (e.Message.Contains("429"))
{
System.Threading.Thread.Sleep(5 * 60 * 1000);
return request.GetResponse();
}
else
{
throw new Exception(e.Message);
}
}
这是处理这种情况的正确方法吗?
您不应该对异常进行字符串解析 class。反过来,Exception classes 不应该将重要信息放入消息字段。
你对异常的整个捕获和重新抛出也是错误的。所以在这方面你应该阅读正确的异常处理。这是我 link 经常阅读的两篇文章:
http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx (死LINK) http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
除非我弄错网络class,我想你应该catch WebExceptions only。我认为这就是您应该如何从此函数中获取 HTTP 错误代码,但我不太确定。
最好完全避免异常。所以我不得不问:为什么你甚至经常连续调用那个函数?批量检索没有合适的功能吗?该服务甚至打算像这样自动化吗?
这里要考虑的一件事是,如果您开始超过限制,您消耗的 api 可能会 "penalize"。我认为最好积极主动地限制您的 api 请求,这样您就永远不会收到 429 错误。
我们开始在我们的应用程序上遇到同样的事情(429 个错误)来自我们正在使用的 api。这个特定 api 的限制是每 60 秒 10 个。我们实现了一个 throttle()
函数,它利用嵌入了 date/time 的内存缓存。我们使用的 api 还以帐户为基础跟踪使用情况。你可能不需要那个。但这是我们用来限制我们的请求以确保我们始终低于限制的片段:
private void throttle()
{
var maxPerPeriod = 250;
//If you utilize multiple accounts, you can throttle per account. If not, don't use this:
var keyPrefix = "a_unique_id_for_the_basis_of_throttling";
var intervalPeriod = 300000;//5 minutes
var sleepInterval = 5000;//period to "sleep" before trying again (if the limits have been reached)
var recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
while (recentTransactions >= maxPerPeriod)
{
System.Threading.Thread.Sleep(sleepInterval);
recentTransactions = MemoryCache.Default.Count(x => x.Key.StartsWith(keyPrefix));
}
var key = keyPrefix + "_" + DateTime.Now.ToUniversalTime().ToString("yyyyMMddHHmm");
var existing = MemoryCache.Default.Where(x => x.Key.StartsWith(key));
if (existing != null && existing.Any())
{
var counter = 2;
var last = existing.OrderBy(x => x.Key).Last();
var pieces = last.Key.Split('_');
if (pieces.Count() > 2)
{
var lastCount = 0;
if (int.TryParse(pieces[2], out lastCount))
{
counter = lastCount + 1;
}
}
key = key + "_" + counter;
}
var policy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(intervalPeriod)
};
MemoryCache.Default.Set(key, 1, policy);
}
我们在代码中使用了这个 throttle()
函数,如下所示:
public override void DoAction()
{
throttle();
var url = ContentUri;
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("Authorization", "Bearer " + AccessToken);
request.Accept = "application/json";
WebResponse response = request.GetResponse();
var dataStream = new MemoryStream();
using (Stream responseStream = request.GetResponse().GetResponseStream())
{
//DO STUFF WITH THE DOWNLOADED DATA HERE...
}
dataStream.Close();
response.Close();
}
它本质上是在缓存中跟踪您的请求。如果已达到限制,它会暂停,直到经过足够的时间,您仍未达到限制。