我有一个api端点供人们调用该服务, 如果缓存过期,我只希望一次运行一个GetCacheToken。
显然我通过getAccessToken同时有3个调用getCacheToken,并且它在等待_client.SendAsync(requestData)时同步处理(不确定原因);它将刷新刷新令牌,而旧的刷新令牌将不再有效。
因此,第一个呼叫通过,第二个和第三个失败,因为它们都使用旧令牌在发送请求中传递了它。
有没有一种方法可以在缓存过期时锁定进程? 由于Lock无法在等待呼叫中使用。 谢谢
认为它将由async await处理,但是没有运气
[System.Web.Http.HttpGet]
[System.Web.Http.Route("getAccessToken")]
public async Task<string> getAccessToken()
{
return await cacheToken.GetCacheToken();
}
private static readonly SemaphoreSlim _mutex = new SemaphoreSlim(1,1);
public async Task<string> GetCacheToken()
{
ObjectCache cache = MemoryCache.Default;
var cacheToken = cache.Get("refreshToken", null);
string refreshToken = cacheToken == null ? GetToken() : cacheToken.ToString();
if (!cache.Contains("apiToken"))
{
//it prevent multiple threads call to update refreshtoken at the same time
await _mutex.WaitAsync();
try
{
//Log.Information("Access token " + cache.Get("apiToken", null));
var isNull = cache.Get("refreshToken", null) == null ? "Yes " : "No ";
var logtime = DateTime.Now.ToString("O");
Log.Information("refresh token is null : " + isNull + DateTime.Now.ToString("O") + " " + logtime);
Log.Information(
"refresh token : " + refreshToken + " " + DateTime.Now.ToString("O") + " " + logtime);
var httpContent = new StringContent("", Encoding.UTF8, "application/x-www-form-urlencoded");
var dict = new Dictionary<string, string>();
dict.Add("grant_type", "refresh_token");
dict.Add("refresh_token", refreshToken);
var requestData = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://oauth2.sky.blackbaud.com/token"),
Content = new FormUrlEncodedContent(dict)
};
requestData.Headers.Authorization = new AuthenticationHeaderValue("Basic", Settings.BasicAuth);
var results = await _client.SendAsync(requestData);
Log.Information("run time " + DateTime.Now);
var resultResponse = results.Content.ReadAsStringAsync().Result;
try
{
results.EnsureSuccessStatusCode();
var result = _js.Deserialize<TokenModel>(resultResponse);
//token expires in one hour from blackbaud
var expiration = DateTimeOffset.Now.AddMinutes(55);
cache.Set("apiToken", result.access_token, expiration);
cache.Set("refreshToken", result.refresh_token, expiration);
await UpdateToken(result.access_token, result.refresh_token);
Log.Information("refresh token after update : " + cache.Get("refreshToken", null) +
DateTime.Now.ToString("O"));
}
catch (Exception e)
{
var exceptionMessage = $"ResultMessage : {resultResponse} Exception: {e}.";
Log.Exception(e, exceptionMessage);
throw;
}
}
finally
{
_mutex.Release();
}
}
return cache.Get("apiToken", null).ToString();
}
这是日志
12 Jul 2019 12:00:10.847
ResultMessage : {"error":"invalid_grant"} Exception: System.Net.Http.HttpRequestException: Response status code does not indicate success: 400 (Bad Request). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at RaisersEdge.Infrastructure.Cache.<GetCacheToken>d__3.MoveNext().Exception: HttpRequestException {"Message":"Response status code does not indicate success: 400 (Bad Request).","Data":[],…Stack: at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at RaisersEdge.Infrastructure.Cache.<GetCacheToken>d__3.MoveNext()Category: Exception
12 Jul 2019 12:00:10.841
run time 12/07/2019 12:00:10 PM
12 Jul 2019 12:00:09.981
refresh token : 03124379add64a90850961e6a2021d6e 2019-07-12T12:00:09.9813412+12:00 2019-07-12T12:00:09.9813412+12:00
12 Jul 2019 12:00:09.981
refresh token is null : No 2019-07-12T12:00:09.9813412+12:00 2019-07-12T12:00:09.9813412+12:00
12 Jul 2019 12:00:09.980
refresh token after update : f0b569cfa2254bbfbd78e9d84ddd66ae2019-07-12T12:00:09.9803523+12:00
12 Jul 2019 12:00:09.951
run time 12/07/2019 12:00:09 PM
12 Jul 2019 12:00:08.970
refresh token : 03124379add64a90850961e6a2021d6e 2019-07-12T12:00:08.9212759+12:00 2019-07-12T12:00:08.9192713+12:00
12 Jul 2019 12:00:08.942
refresh token is null : Yes 2019-07-12T12:00:08.9192713+12:00 2019-07-12T12:00:08.9192713+12:00
答案 0 :(得分:1)
lock
的异步等效项是SemaphoreSlim
。
如果您想要的代码如下所示:
private readonly object _mutex = new object();
...
lock (_mutex)
{
...
var results = await _client.SendAsync(requestData);
...
}
然后您可以使用SemaphoreSlim
这样获得所需的行为:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim();
...
await _mutex.WaitAsync();
try
{
...
var results = await _client.SendAsync(requestData);
...
}
finally
{
_mutex.Release();
}