【发布时间】:2014-08-12 05:27:28
【问题描述】:
我正在使用 async/await 每秒向使用其 API 的服务发出多个请求。我遇到的问题是当我需要刷新令牌时(它每小时过期)。令牌过期后,我从服务返回 401 未经授权的错误。那是我刷新令牌并再次重试失败的请求的时候。令牌刷新得很好,但我发现即使在刷新令牌之后,仍然会使用旧令牌发送许多后续请求。以下是此功能中使用的方法。想知道是否有什么突出的东西是造成这种意外行为的罪魁祸首。
public void Process(id)
{
var tasks = items.Select(async item =>
{
var response = await SendRequestAsync(() => CreateRequest(item.Url));
//do something with response
await Process(item.subId); //recursive call to process sub items.
}).ToList();
if (tasks.Any())
await Task.WhenAll(tasks);
}
public HttpRequestMessage CreateRequest(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Authorization", "Bearer " + AppSettings.AccessToken);
return request;
}
public async Task<HttpResponseMessage> SendRequestAsync(Func<HttpRequestMessage> funcReq)
{
var response = await ExecuteRequestAsync(funcReq());
while (response.StatusCode == HttpStatusCode.Unauthorized)
{
await RefreshTokenAsync();
return await ExecuteRequestAsync(funcReq()); //assuming func ensures that CreateRequest is called each time, so I'll always have a new request with the updated token.
}
return response;
}
private async Task<HttpResponseMessage> ExecuteRequestAsync(HttpRequestMessage request)
{
var client = new HttpClient();
var response = await client.SendAsync(request);
return response;
}
public async Task RefreshTokenAsync()
{
await semaphoreSlim.WaitAsync();
try
{
if ((DateTime.Now - refreshTime).TotalMinutes < 60) //tokens last for an hour, so after the refresh is made by the first request that failed, subsequent requests should have the latest token.
return;
Token newToken = GetNewToken();
AppSettings.AccessToken = newToken.AccessToken //AppSettings is a singleton wrapper class for app.cofig app settings
refreshTime = DateTime.Now
}
finally
{
semaphoreSlim.Release();
}
}
【问题讨论】:
-
使用相同令牌执行请求的时间有多长?您是否消除了这些请求在刷新令牌之前正在进行/启动的可能性?
-
@MarcelN。起初我收到一堆 401 错误——这是意料之中的。但是我的信号量应该确保只有第一个失败的请求才会刷新令牌。其余的应该在它们被发送时具有更新的令牌。尽管有些请求确实使用新令牌发出,但仍有许多请求使用旧令牌发出。刷新后,一些重试请求使用旧令牌持续了一个多小时。
-
您的请求需要多长时间?假设@MarcelN 关于先前请求未完成执行的说法听起来很合理。另外,你是如何初始化你的
SemaphoreSlim的? -
我认为这并不完全正确。某些请求可能已经在此时
var response = await ExecuteRequestAsync(funcReq());,在评估funcReq之后,当令牌被刷新时。到这个时候,他们获得新令牌为时已晚。他们将与旧的一起被处决。 -
@Prabhu:我不确定更好,但您可以不等待 401 来刷新令牌。相反,每 60 分钟刷新一次并仅在重新生成时序列化访问。这意味着在
CreateRequest中,您将不再使用AppSettings.AccessToken,而是使用 getter 来完成这项工作。这样你就可以肯定你总是拥有最新的令牌。缺点是如果 API 更改了您必须调整的时间间隔。
标签: c# .net asynchronous async-await task