為什麼需要緩存 Access Token
- 微信規定
access_token 有效期 7200 秒(2 小時)。
- 頻繁請求會觸發接口限流(每日調用上限 2000 次)。
- 多進程/分佈式環境下需共享緩存,避免重複獲取導致舊 token 失效。
方案一:文件存儲(單機/簡單項目)
function getAccessToken() {
$file = __DIR__ . '/access_token.json';
$data = file_exists($file) ? json_decode(file_get_contents($file), true) : ['expire_time' => 0];
if ($data['expire_time'] < time()) {
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
$res = json_decode(file_get_contents($url), true);
if (isset($res['access_token'])) {
$data['access_token'] = $res['access_token'];
$data['expire_time'] = time() + 7000; // 提前 200 秒刷新
file_put_contents($file, json_encode($data));
} else {
throw new Exception('獲取失敗: ' . ($res['errmsg'] ?? 'unknown'));
}
}
return $data['access_token'];
}
方案二:Memcache 存儲(分佈式/高併發)
function getAccessToken($appid, $appsecret) {
$mem = new Memcache();
$mem->connect('127.0.0.1', 11211);
$key = "access_token_{$appid}";
$token = $mem->get($key);
if ($token) return $token;
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}";
$res = json_decode(file_get_contents($url), true);
if (isset($res['access_token'])) {
$mem->set($key, $res['access_token'], 0, 7000); // 過期時間 7000 秒
return $res['access_token'];
}
throw new Exception('獲取失敗');
}
進階:Redis + 分佈式鎖防止緩存擊穿
function getAccessTokenRedis($appid, $appsecret) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = "wx:access_token:{$appid}";
$lockKey = "wx:lock:access_token:{$appid}";
$token = $redis->get($key);
if ($token) return $token;
// 獲取分佈式鎖
$lock = $redis->set($lockKey, 1, ['nx', 'ex' => 10]);
if ($lock) {
try {
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}";
$res = json_decode(file_get_contents($url), true);
if (isset($res['access_token'])) {
$redis->setex($key, 7000, $res['access_token']);
return $res['access_token'];
}
} finally {
$redis->del($lockKey);
}
} else {
// 等待並重試
usleep(50000);
return getAccessTokenRedis($appid, $appsecret);
}
}
最佳實踐總結
| 維度 | 文件存儲 | Memcache | Redis(推薦) |
| 適用場景 | 單機、低併發 | 多機、中等併發 | 高併發、分佈式、需持久化 |
| 原子操作 | 需自行加文件鎖 | 不支持原子獲取並設置 | 支持 SET NX EX / Lua 腳本 |
| 數據結構 | 簡單 Key-Value | 簡單 Key-Value | 豐富(Hash、List、Set...) |
| 高可用 | 無 | 需自建集群 | 支持 Sentinel/Cluster |
關鍵提示
- 提前刷新:緩存過期時間設為 7000 秒(< 7200),留出網絡波動緩衝。
- 異常重試:獲取失敗時應有指數退避重試機制,並記錄告警日誌。
- 多公眾號隔離:Key 必須包含
appid,避免相互覆蓋。