优化 Token 存储:使用绝对过期缓存
之前的项目中,每次都去微信服务器获取新的 AccessToken,这样肯定不好。微信服务器返回的 Token 有效期是 7200 秒(2 小时),在这期间 Token 都是有效的,没必要每次都重新获取,频繁调用也会影响程序性能。
我们可以通过 ASP.NET 缓存(Cache) 把第一次获取到的 Token 存起来,设置 绝对过期时间 为 7200 秒,到期自动失效,下次再重新获取。
为什么用绝对过期而不是滑动过期?
- 绝对过期(Absolute Expiration):从加入缓存那一刻起,到指定时间点就过期,不管期间有没有被访问。正好契合微信 Token 的 7200 秒有效期。
- 滑动过期(Sliding Expiration):每次访问缓存就刷新过期时间,这会导致 Token 永远不过期,但微信服务器那边其实已经失效了,不能用。
所以这里使用 System.Web.Caching.Cache.NoSlidingExpiration 禁用滑动过期,配合 DateTime.Now.AddSeconds(7200) 实现绝对过期。
缓存获取/加载通用方法
private static object _lock = new object();
private static T CacheOrLoad<T>(string key, Func<T> action)
{
T token = default(T);
lock (_lock)
{
// 1. 先从缓存里取
token = (T)System.Web.HttpRuntime.Cache.Get(key);
if (token == null)
{
// 2. 缓存里没有,调用委托去微信服务器获取新 token
token = action();
// 3. 加入缓存,绝对过期时间 7200 秒,非滑动过期
System.Web.HttpRuntime.Cache.Add(
key,
token,
null,
DateTime.Now.AddSeconds(7200),
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,
null
);
}
}
return token;
}Code language: PHP (php)
调用示例
public static string GetAccessToken()
{
return CacheOrLoad<string>("Token_KEY", () =>
{
// 这里实际请求微信服务器获取 token
// 伪代码:return HttpGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret);
return "xxxdsfstirjflds;fjdsoifdslfdsf"; // 请求微信服务器获取token
});
}Code language: JavaScript (javascript)
控制台测试程序
static void Main(string[] args)
{
var result = CacheOrLoad("Token_KEY", () =>
{
return "xxxdsfstirjflds;fjdsoifdslfdsf"; // 请求微信服务器获取token
});
Console.WriteLine(result);
Console.ReadKey();
}Code language: JavaScript (javascript)
应用到现有项目中的要点
- 加锁:使用
lock (_lock)防止并发时多个请求同时去微信服务器取 Token,避免短时间内多次调用接口被限频。 - 缓存键唯一:不同公众号、不同用途的 Token 用不同的 key,比如
"Token_KEY"、"WebAccessToken_KEY"等。 - 异常处理:如果获取 Token 失败(网络异常、appid/secret 错误),不应该把错误结果写入缓存,可以在
action里抛异常或返回 null,在CacheOrLoad里判断 token 有效性再决定是否缓存。 - 网页授权 access_token 也适用:网页授权的 access_token 有效期也是 7200 秒,同样可以用这个模式缓存,但注意它的 key 要区分 openid 或 session。
改进版(带有效性校验)
private static T CacheOrLoad<T>(string key, Func<T> action) where T : class
{
lock (_lock)
{
var token = System.Web.HttpRuntime.Cache.Get(key) as T;
if (token == null)
{
token = action();
if (token != null)
{
System.Web.HttpRuntime.Cache.Add(
key,
token,
null,
DateTime.Now.AddSeconds(7200),
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,
null
);
}
}
return token;
}
}Code language: PHP (php)
这样改造后,Token 在 2 小时内只会向微信服务器请求一次,大幅减少不必要的网络调用,也符合微信接口调用的最佳实践。