17-Token使用缓存的方式来存储

优化 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)

应用到现有项目中的要点

  1. 加锁:使用 lock (_lock) 防止并发时多个请求同时去微信服务器取 Token,避免短时间内多次调用接口被限频。
  2. 缓存键唯一:不同公众号、不同用途的 Token 用不同的 key,比如 "Token_KEY"、"WebAccessToken_KEY" 等。
  3. 异常处理:如果获取 Token 失败(网络异常、appid/secret 错误),不应该把错误结果写入缓存,可以在 action 里抛异常或返回 null,在 CacheOrLoad 里判断 token 有效性再决定是否缓存。
  4. 网页授权 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 小时内只会向微信服务器请求一次,大幅减少不必要的网络调用,也符合微信接口调用的最佳实践。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注