如何让Azure Function在多次调用时保持活跃并在1小时内复用会话与缓存OAuth AccessToken
一、保持同一个Azure Function实例活跃
Azure Function的实例是平台自动调度的,空闲一段时间后会被回收(也就是常说的“冷启动”),要固定复用同一个实例,你可以试试这几个实用方法:
启用预热触发器(Warm-up Trigger):这是Azure官方为预热实例提供的触发器,只要你的函数应用用的是弹性Premium计划或Dedicated(App Service)计划(消费计划不支持),就能配置它。在函数的
function.json里添加warmup类型的触发器后,实例启动时会立刻触发这个逻辑,平台也会尽量保留该实例处于活跃状态,避免被快速回收。配置实例保留策略:在弹性Premium计划下,把
Minimum Elastic Workers设为1,同时在App Service配置里开启Always On,这样平台会至少维持一个实例在线,不会因为空闲就回收。同样要注意,消费计划不支持Always On,必须升级到付费计划才行。定时“唤醒”函数:如果只能用消费计划,那就搞个Timer Trigger辅助函数,每隔10分钟左右调用一次你的目标Http Trigger函数,让实例一直处于活跃状态。这种方法有点取巧,但胜在不需要升级计划,只是会产生额外的调用次数,要权衡成本。
二、复用会话(缓存AccessToken)
要避免频繁调用OAuth端点,核心是做好令牌缓存+过期管理,这里有两种适配不同场景的方案:
方案1:静态变量缓存(单实例场景)
如果你已经通过上面的方法固定了单个实例,用静态变量缓存令牌是最简单的方式:
private static string _cachedAccessToken; private static DateTime _tokenExpiryTime; public static async Task<IActionResult> Run(HttpRequest req, ILogger log) { // 检查令牌是否还在有效期内 if (string.IsNullOrEmpty(_cachedAccessToken) || DateTime.UtcNow >= _tokenExpiryTime) { // 调用GetAccessToken()获取新令牌 var tokenResult = await GetAccessToken(); _cachedAccessToken = tokenResult.AccessToken; // 预留5分钟缓冲时间,避免令牌刚过期就有请求进来 _tokenExpiryTime = DateTime.UtcNow.AddMinutes(tokenResult.ExpiresIn - 5); } return new OkObjectResult(_cachedAccessToken); }
这种方法直接在实例内存里存令牌,复用效率极高,但仅限单个实例的情况——如果函数扩缩容到多个实例,每个实例都会自己去拿令牌,达不到共享的效果。
方案2:Azure Cache for Redis(多实例/分布式场景)
如果你的函数可能会自动扩缩容,或者需要多个实例共享令牌,那就用Azure Cache for Redis做分布式缓存:
- 先创建一个Azure Cache for Redis实例,拿到连接字符串;
- 在函数里引入Redis客户端(比如StackExchange.Redis),然后按下面的逻辑实现:
private static readonly ConnectionMultiplexer _redis = ConnectionMultiplexer.Connect("你的Redis连接字符串"); public static async Task<IActionResult> Run(HttpRequest req, ILogger log) { var db = _redis.GetDatabase(); var cachedToken = await db.StringGetAsync("oauth_access_token"); var expiryTimeStr = await db.StringGetAsync("oauth_token_expiry"); // 检查缓存是否有效 if (!cachedToken.HasValue || DateTime.UtcNow >= DateTime.Parse(expiryTimeStr)) { var tokenResult = await GetAccessToken(); await db.StringSetAsync("oauth_access_token", tokenResult.AccessToken); await db.StringSetAsync("oauth_token_expiry", DateTime.UtcNow.AddMinutes(tokenResult.ExpiresIn - 5).ToString()); cachedToken = tokenResult.AccessToken; } return new OkObjectResult(cachedToken); }
不管多少个实例,都会从同一个Redis缓存里拿令牌,完美匹配你“1小时内多次调用用相同会话”的需求,而且Redis性能极高,不会成为系统瓶颈。
关键注意点
- 一定要给令牌设置缓冲过期时间,比如令牌有效期是1小时,就设置缓存55分钟后过期,避免在令牌刚好过期的瞬间有请求进来,导致重复调用OAuth端点;
- 如果你的OAuth流程支持刷新令牌,建议在缓存过期前用刷新令牌获取新令牌,而不是重新走完整的授权流程,效率会更高。
对应你的调用场景验证
- 第1次调用:缓存为空,调用GetAccessToken()拿到
bearer ey03193013459,存入缓存并设置55分钟后过期; - 30分钟后第2次调用:缓存未过期,直接返回
bearer ey03193013459; - 2小时后第3次调用:缓存已过期,重新调用GetAccessToken()拿到新令牌
bearer ba204345314555,存入缓存。
内容的提问来源于stack exchange,提问作者Just J

