Skip to content

缓存

EasyWeChat 6.x 使用 symfony/cache 组件来处理缓存,它支持目前几乎所有主流的缓存引擎。

在 SDK 中,所有缓存默认使用文件系统缓存,缓存路径取决于 PHP 的临时目录。如果你需要自定义缓存配置,可以通过简单的几个步骤来实现。

默认缓存行为

EasyWeChat 6.x 中的应用实例(如 OfficialAccount\ApplicationMiniApp\Application 等)都通过 InteractWithCache trait 提供了统一的缓存接口:

php
use EasyWeChat\OfficialAccount\Application;

$app = new Application([
    'app_id' => 'your-app-id',
    'secret' => 'your-app-secret',
    // ...
]);

// 获取默认缓存实例
$cache = $app->getCache();

// 缓存默认配置
echo $app->getCacheLifetime(); // 1500 秒
echo $app->getCacheNamespace(); // 'easywechat'

缓存配置调整

你可以调整缓存的生命周期和命名空间:

php
// 设置缓存生命周期为 3600 秒(1小时)
$app->setCacheLifetime(3600);

// 设置缓存命名空间
$app->setCacheNamespace('my_wechat_app');

使用 Redis 缓存

基础 Redis 配置

使用 Redis 作为缓存引擎是最常见的需求。首先安装 Redis 相关扩展:

bash
composer require predis/predis

然后配置 Redis 缓存:

php
use EasyWeChat\OfficialAccount\Application;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

// 创建应用实例
$app = new Application([
    'app_id' => 'your-app-id',
    'secret' => 'your-app-secret',
]);

// 创建 Redis 连接
$redis = new \Predis\Client([
    'scheme' => 'tcp',
    'host'   => '127.0.0.1',
    'port'   => 6379,
]);

// 创建 Redis 缓存适配器
$cache = new Psr16Cache(
    new RedisAdapter($redis, 'easywechat', 1500)
);

// 设置自定义缓存
$app->setCache($cache);

使用 Redis 集群

对于 Redis 集群环境:

php
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

// Redis 集群配置
$redis = new \Predis\Client([
    [
        'scheme' => 'tcp',
        'host'   => '10.0.0.1',
        'port'   => 6379,
    ],
    [
        'scheme' => 'tcp', 
        'host'   => '10.0.0.2',
        'port'   => 6379,
    ],
], [
    'cluster' => 'redis',
]);

$cache = new Psr16Cache(
    new RedisAdapter($redis, 'easywechat_cluster', 1500)
);

$app->setCache($cache);

使用 phpredis 扩展

如果你使用 phpredis 扩展:

php
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

// 使用 phpredis
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$redis->select(1); // 选择数据库

$cache = new Psr16Cache(
    new RedisAdapter($redis, 'easywechat', 1500)
);

$app->setCache($cache);

使用 Memcached 缓存

php
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
use Symfony\Component\Cache\Psr16Cache;

// 创建 Memcached 连接
$memcached = new \Memcached();
$memcached->addServer('127.0.0.1', 11211);

// 创建缓存实例
$cache = new Psr16Cache(
    new MemcachedAdapter($memcached, 'easywechat', 1500)
);

$app->setCache($cache);

在 Laravel 中使用

Laravel 框架提供了便捷的缓存管理,你可以直接使用 Laravel 的缓存驱动:

使用 Laravel Cache

php
use Symfony\Component\Cache\Adapter\Psr6Adapter;
use Symfony\Component\Cache\Psr16Cache;

// 将 Laravel Cache 转换为 PSR-16 缓存
$cache = new Psr16Cache(
    new Psr6Adapter(
        app('cache')->store() // 使用默认缓存驱动
    )
);

$app->setCache($cache);

使用指定的 Laravel Cache Store

php
// 使用 Redis 作为缓存驱动
$cache = new Psr16Cache(
    new Psr6Adapter(
        app('cache')->store('redis')
    )
);

$app->setCache($cache);

Laravel 服务提供者中配置

在 Laravel 服务提供者中统一配置:

php
// app/Providers/WeChatServiceProvider.php
use EasyWeChat\OfficialAccount\Application;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Cache\Adapter\Psr6Adapter;
use Symfony\Component\Cache\Psr16Cache;

class WeChatServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton('wechat.official_account', function ($app) {
            $application = new Application(config('wechat.official_account'));
            
            // 使用 Laravel 缓存
            $cache = new Psr16Cache(
                new Psr6Adapter(
                    $app['cache']->store(config('wechat.cache_store', 'redis'))
                )
            );
            
            $application->setCache($cache);
            
            return $application;
        });
    }
}

自定义缓存驱动

如果现有的缓存驱动无法满足你的需求,你可以实现自己的缓存类,只需实现 PSR-16 规范即可。

实现 PSR-16 接口

PSR-16 CacheInterface 接口包含以下方法:

php
interface CacheInterface
{
    public function get($key, $default = null);
    public function set($key, $value, $ttl = null);
    public function delete($key);
    public function clear();
    public function getMultiple($keys, $default = null);
    public function setMultiple($values, $ttl = null);
    public function deleteMultiple($keys);
    public function has($key);
}

自定义缓存实现示例

php
use Psr\SimpleCache\CacheInterface;

class MyCustomCache implements CacheInterface
{
    private array $data = [];

    public function get($key, $default = null)
    {
        return $this->data[$key] ?? $default;
    }

    public function set($key, $value, $ttl = null)
    {
        // 这里可以实现你的存储逻辑
        // 比如存储到数据库、文件或其他存储系统
        $this->data[$key] = $value;
        
        return true;
    }

    public function delete($key)
    {
        unset($this->data[$key]);
        return true;
    }

    public function clear()
    {
        $this->data = [];
        return true;
    }

    public function getMultiple($keys, $default = null)
    {
        $result = [];
        foreach ($keys as $key) {
            $result[$key] = $this->get($key, $default);
        }
        return $result;
    }

    public function setMultiple($values, $ttl = null)
    {
        foreach ($values as $key => $value) {
            $this->set($key, $value, $ttl);
        }
        return true;
    }

    public function deleteMultiple($keys)
    {
        foreach ($keys as $key) {
            $this->delete($key);
        }
        return true;
    }

    public function has($key)
    {
        $stmt = $this->pdo->prepare("
            SELECT 1 FROM cache_items 
            WHERE cache_key = ? AND (expires_at IS NULL OR expires_at > NOW())
        ");
        $stmt->execute([$key]);
        return $stmt->fetchColumn() !== false;
    }
}

使用自定义缓存

php
// 实例化自定义缓存
$myCache = new MyCustomCache();

// 应用到 EasyWeChat
$app->setCache($myCache);

数据库缓存实现

下面是一个使用数据库作为缓存存储的完整示例:

php
use Psr\SimpleCache\CacheInterface;

class DatabaseCache implements CacheInterface
{
    private \PDO $pdo;
    
    public function __construct(\PDO $pdo)
    {
        $this->pdo = $pdo;
        $this->createTable();
    }
    
    private function createTable()
    {
        $sql = "
            CREATE TABLE IF NOT EXISTS cache_items (
                cache_key VARCHAR(255) PRIMARY KEY,
                cache_value TEXT,
                expires_at TIMESTAMP NULL
            )
        ";
        $this->pdo->exec($sql);
    }

    public function get($key, $default = null)
    {
        $stmt = $this->pdo->prepare("
            SELECT cache_value FROM cache_items 
            WHERE cache_key = ? AND (expires_at IS NULL OR expires_at > NOW())
        ");
        $stmt->execute([$key]);
        
        $result = $stmt->fetchColumn();
        
        if ($result === false) {
            return $default;
        }
        
        return unserialize($result);
    }

    public function set($key, $value, $ttl = null)
    {
        $expiresAt = $ttl ? date('Y-m-d H:i:s', time() + $ttl) : null;
        
        $stmt = $this->pdo->prepare("
            REPLACE INTO cache_items (cache_key, cache_value, expires_at) 
            VALUES (?, ?, ?)
        ");
        
        return $stmt->execute([
            $key, 
            serialize($value), 
            $expiresAt
        ]);
    }

    public function delete($key)
    {
        $stmt = $this->pdo->prepare("DELETE FROM cache_items WHERE cache_key = ?");
        return $stmt->execute([$key]);
    }

    public function clear()
    {
        return $this->pdo->exec("DELETE FROM cache_items") !== false;
    }

    public function getMultiple($keys, $default = null)
    {
        $result = [];
        foreach ($keys as $key) {
            $result[$key] = $this->get($key, $default);
        }
        return $result;
    }

    public function setMultiple($values, $ttl = null)
    {
        foreach ($values as $key => $value) {
            $this->set($key, $value, $ttl);
        }
        return true;
    }

    public function deleteMultiple($keys)
    {
        foreach ($keys as $key) {
            $this->delete($key);
        }
        return true;
    }

    public function has($key)
    {
        $stmt = $this->pdo->prepare("
            SELECT 1 FROM cache_items 
            WHERE cache_key = ? AND (expires_at IS NULL OR expires_at > NOW())
        ");
        $stmt->execute([$key]);
        return $stmt->fetchColumn() !== false;
    }
}

// 使用示例
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$cache = new DatabaseCache($pdo);

$app->setCache($cache);

缓存性能优化建议

1. 选择合适的缓存驱动

  • Redis: 适用于分布式环境,支持丰富的数据结构
  • Memcached: 简单高效,适用于纯缓存场景
  • 文件缓存: 适用于单机环境或小型应用
  • 数据库缓存: 适用于需要持久化的场景

2. 合理设置缓存时间

php
// 根据数据更新频率设置不同的缓存时间
$app->setCacheLifetime(7200); // Access Token 缓存 2 小时

3. 使用缓存前缀避免冲突

php
// 为不同环境设置不同的命名空间
$app->setCacheNamespace('easywechat_prod'); // 生产环境
$app->setCacheNamespace('easywechat_dev');  // 开发环境

4. 监控缓存命中率

php
// 在自定义缓存中添加统计功能
class MonitoredCache implements CacheInterface 
{
    private $hits = 0;
    private $misses = 0;
    
    public function get($key, $default = null) 
    {
        $value = $this->actualCache->get($key, $default);
        
        if ($value === $default) {
            $this->misses++;
        } else {
            $this->hits++;
        }
        
        return $value;
    }
    
    public function getHitRate(): float
    {
        $total = $this->hits + $this->misses;
        return $total > 0 ? $this->hits / $total : 0;
    }
}

常见问题

Q: 如何清空 EasyWeChat 的缓存?

php
// 清空当前应用的所有缓存
$app->getCache()->clear();

// 删除特定缓存项
$app->getCache()->delete('specific_key');

Q: 如何在不同的应用间共享缓存?

php
// 使用相同的缓存实例
$sharedCache = new Psr16Cache(new RedisAdapter($redis));

$officialAccount->setCache($sharedCache);
$miniApp->setCache($sharedCache);

Q: 缓存键冲突怎么办?

php
// 为不同应用设置不同的命名空间
$officialAccount->setCacheNamespace('wechat_oa');
$miniApp->setCacheNamespace('wechat_mini');

通过合理配置缓存,你可以显著提升 EasyWeChat 应用的性能和响应速度。选择适合你业务场景的缓存方案,并根据实际使用情况进行优化调整。

Released under the MIT License.