强制异步方法按需延迟调用,并在前一个结果过期时再次调用 [英] Enforce an async method to be called lazily on demand, and called again when the previous result has expired

查看:31
本文介绍了强制异步方法按需延迟调用,并在前一个结果过期时再次调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个异步 Web API,它需要在每个 API 调用中将 AccessToken(一个不可变结构)作为参数传递.这个AccessToken本身是通过调用同一个Web API的异步Authenticate方法获得的.

I am consuming an asynchronous Web API that requires an AccessToken (an immutable struct) to be passed as an argument on every API call. This AccessToken is itself obtained by calling an asynchronous Authenticate method of the same Web API.

class WebApi
{
    public Task<AccessToken> Authenticate(string username, string password);
    public Task PurchaseItem(AccessToken token, int itemId, int quantity);
    // More methods having an AccessToken parameter
}

出于性能原因,我不想在调用 API 的所有其他方法之前调用 Authenticate 方法.我想调用一次,然后对多个 API 调用重复使用相同的 AccessToken.我的问题是 AccessToken 每 15 分钟过期一次,调用任何具有过期 AccessToken 的 API 方法都会导致 AccessTokenExpiredException.我可以捕获此异常,然后在获取新的 AccessToken 后重试错误调用,但出于性能原因,我更愿意在 AccessToken 过期之前抢先刷新它.我的应用程序是多线程的,因此多个线程可能会尝试同时使用/刷新相同的 AccessToken 值,事情很快就会变得非常混乱.

I don't want to call the Authenticate method before calling every other method of the API, for performance reasons. I want to call it once, and then reuse the same AccessToken for multiple API calls. My problem is that the AccessToken is expiring every 15 minutes, and calling any API method with an expired AccessToken results to an AccessTokenExpiredException. I could catch this exception and then retry the faulted call, after acquiring a new AccessToken, but I would prefer to preemptively refresh the AccessToken before it has expired, again for performance reasons. My application is multithreaded, so multiple threads might try to use/refresh the same AccessToken value concurrently, and things quickly start to become very messy.

要求是:

  1. Authenticate 方法的调用频率不应超过每 15 分钟一次,即使多个线程尝试同时调用 Web API 的方法.
  2. 如果 Authenticate 调用失败,下次需要 AccessToken 时应重复调用.此要求优先于先前的要求.将出现故障的 Task 缓存和重用 15 分钟是不可接受的.
  3. Authenticate 方法仅在实际需要 AccessToken 时才应调用.使用 Timer 每 15 分钟调用一次是不可接受的.
  4. AccessToken 只能在创建后的 15 分钟内使用.
  5. 过期机制不应依赖于系统时钟.系统时钟调整不应影响(延长或缩短)有效期.
  1. The Authenticate method should not be called more frequently than once every 15 minutes, even if multiple threads attempt to invoke methods of the Web API concurrently.
  2. In case an Authenticate call fails, it should be repeated the next time an AccessToken is needed. This requirement takes precedence over the previous requirement. Caching and reusing a faulted Task<AccessToken> for 15 minutes is not acceptable.
  3. The Authenticate method should be called only when an AccessToken is actually needed. Invoking it every 15 minutes with a Timer is not acceptable.
  4. An AccessToken should only be used during the next 15 minutes after its creation.
  5. The expiration mechanism should not be dependent on the system clock. A system-wise clock adjustment should not affect (elongate or shorten) the expiration period.

我的问题是:怎么可能我以一种满足要求的方式抽象了获取、监控到期和刷新 AccessToken 的功能,同时保持应用程序的其余部分不受所有这些复杂性的影响?我在想类似于我在这个问题中找到的 AsyncLazy 类型的东西:强制调用一次异步方法,但增强具有过期功能.以下是使用此类型的假设示例(使用 TimeSpan 参数增强):

My question is: how could I abstract the functionality of acquiring, monitoring the expiration, and refreshing the AccessToken, in a way that satisfies the requirements, while keeping the rest of my application clean from all this complexity? I am thinking of something similar to the AsyncLazy<T> type that I found in this question: Enforce an async method to be called once, but enhanced with expiration functionality. Here is a hypothetical example of using this type (enhanced with a TimeSpan parameter):

private readonly WebApi _webApi = new WebApi();
private readonly AsyncLazy<AccessToken> _accessToken = new AsyncLazy<AccessToken>(
    () => _webApi.Authenticate("xxx", "yyy"), TimeSpan.FromMinutes(15));

async Task Purchase(int itemId, int quantity)
{
    await _webApi.PurchaseItem(await _accessToken, itemId, quantity);
}

顺便说一句,这个问题的灵感来自 自我回答这个问题,但欢迎任何贡献作为答案.

Btw this question was inspired by a recent question, where the OP was trying to solve a similar problem in a different way. The example presented above is contrived. My intention is to self-answer this question, but any contribution as an answer is welcome.

我想避免在评论中发布答案.如果需要澄清,请使用评论要求对此问题进行澄清.

I would like to ask to avoid posting answers in the comments. Please use the comments to ask for clarifications about this question, in case clarifications are needed.

推荐答案

A resettable"AsyncLazy等效于单个项目异步缓存.在这种情况下,使用基于时间的到期,相似性更加惊人.

A "resettable" AsyncLazy<T> is equivalent to a single-item asynchronous cache. In this case, with a time-based expiration, the similarity is even more striking.

我建议使用实际的AsyncCache;我有 一个我正在研究,目前正在使用一个非常低负载的产品-类似环境,但还没有在实际生产环境中得到很好的测试.

I recommend using an actual AsyncCache<T>; I have one I'm working on and am currently using in a very low-load prod-like environment, but it hasn't been well tested in a real production environment.

这篇关于强制异步方法按需延迟调用,并在前一个结果过期时再次调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆