Defective Code Logo

Total Downloads Latest Stable Version Latest Stable Version

English | العربية | বাংলা | Bosanski | Deutsch | Español | Français | हिन्दी | Italiano | 日本語 | 한국어 | मराठी | Português | Русский | Kiswahili | தமிழ் | తెలుగు | Türkçe | اردو | Tiếng Việt | 中文

引言

Recall 是一个高性能的 Redis 客户端缓存包,专为 Laravel 设计。它利用 Redis 6 的 客户端缓存 功能,结合自动失效,极大地减少了 Redis 的往返次数和延迟。该包专为 Laravel Octane 环境构建,使用 APCu 或 Swoole Table 作为本地缓存层,并通过失效消息与 Redis 保持同步。

当你从 Redis 中获取一个值时,Recall 会将其存储在本地。当该值在 Redis 中发生变化(来自任何客户端)时,Redis 会自动通知 Recall 以使本地副本失效。这为你提供了内存缓存的速度和 Redis 的一致性保证。

主要特性

示例

// 将 recall 配置为你的缓存驱动
// config/cache.php
'stores' => [
'recall' => [
'driver' => 'recall',
],
],
 
// 像使用任何 Laravel 缓存一样使用
use Illuminate\Support\Facades\Cache;
 
// 第一次调用:从 Redis 获取并存储到本地
$user = Cache::store('recall')->get('user:1');
 
// 随后的调用:从本地 APCu/Swoole Table 提供(微秒)
$user = Cache::store('recall')->get('user:1');
 
// 当 user:1 在任何地方被更新时,Redis 通知 Recall 使其失效
Cache::store('recall')->put('user:1', $newUserData, 3600);
// 所有工作线程上的本地缓存会自动失效

安装

通过 Composer 安装该包:

composer require defectivecode/laravel-recall

需求

使用

基本设置

  1. 在你的 config/cache.php 中添加 Recall 缓存存储:
'stores' => [
// ... 其他存储
 
'recall' => [
'driver' => 'recall',
],
],
  1. 在你的应用程序中使用缓存存储:
use Illuminate\Support\Facades\Cache;
 
// 存储一个值(写入 Redis)
Cache::store('recall')->put('key', 'value', 3600);
 
// 检索一个值(第一次调用命中 Redis,后续调用使用本地缓存)
$value = Cache::store('recall')->get('key');
 
// 删除一个值
Cache::store('recall')->forget('key');

工作原理

  1. 第一次读取:从 Redis 获取值并存储在本地 APCu/Swoole Table 缓存中
  2. 后续读取:直接从本地内存提供值(亚毫秒)
  3. 随处写入:当任何客户端在 Redis 中修改该键时,Redis 会发送失效消息
  4. 自动失效:Recall 接收消息并从本地缓存中移除该键
  5. 下次读取:从 Redis 获取新值并重新缓存到本地

这个模式在 Laravel Octane 环境中尤其强大,因为工作线程在请求之间保持不变,使得本地缓存可以积累并从内存中服务多个请求。

Octane 集成

当 Laravel Octane 可用时,Recall 会自动集成:

无需额外配置。当安装 Okctane 时,集成是自动发生的。

配置

发布配置文件:

php artisan vendor:publish --tag=recall-config

这会创建 config/recall.php,包含以下选项:

启用/禁用

'enabled' => env('RECALL_ENABLED', true),

当禁用时,Recall 会直接传递到 Redis,而不使用本地缓存层。这在调试或逐步推出时很有用。

Redis 存储

'redis_store' => env('RECALL_REDIS_STORE', 'redis'),

用于 Redis 操作的 Laravel 缓存存储。它应该引用在你的 config/cache.php 中配置的 Redis 存储。

缓存前缀

'cache_prefixes' => [],

仅缓存匹配这些前缀的键。如果留空则缓存所有键。

// 仅本地缓存用户和设置键
'cache_prefixes' => ['users:', 'settings:', 'config:'],

当你有高频率变更且不应本地缓存的大量键时,这很有用。

本地缓存配置

'local_cache' => [
// 驱动:"apcu" 或 "swoole"
'driver' => env('RECALL_LOCAL_DRIVER', 'apcu'),
 
// 本地缓存键的前缀
'key_prefix' => env('RECALL_LOCAL_PREFIX', 'recall:'),
 
// 默认 TTL(秒数,防止失效被遗漏)
'default_ttl' => (int) env('RECALL_LOCAL_TTL', 3600),
 
// Swoole Table 大小(2 的幂,仅适用于 swoole 驱动)
'table_size' => (int) env('RECALL_SWOOLE_TABLE_SIZE', 65536),
 
// Swoole Table 中每个值的最大字节数(仅适用于 swoole 驱动)
'value_size' => (int) env('RECALL_SWOOLE_VALUE_SIZE', 8192),
],

APCu 驱动(默认)

APCu 驱动可与所有 PHP 环境和 Octane 服务器(Swoole、RoadRunner、FrankenPHP)一起使用。它将缓存值存储在所有 PHP 进程可访问的共享内存中。

要求:

Swoole Table 驱动

Swoole Table 驱动使用 Swoole 的共享内存表,提供跨同一工作线程中的协程一致访问。最适用于 Swoole/OpenSwoole 环境。

配置提示:

Octane 监听器

'listeners' => [
// 工作线程启动时预热连接(减少第一次请求的延迟)
'warm' => env('RECALL_LISTEN_WARM', true),
 
// 在 tick 事件中处理失效(响应更快,稍有开销)
'tick' => env('RECALL_LISTEN_TICK', false),
],

预热连接

启用时,Recall 在 Octane 工作线程启动时建立 Redis 失效订阅。这消除了第一次请求的连接延迟。

Tick 处理

启用时,Recall 在 Octane tick 事件中处理失效消息,除了请求事件之外。这提供了更灵活的缓存失效,代价是稍微增加的开销。

高级用法

手动失效处理

如果你需要手动处理失效(例如,在长时间运行的进程中):

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
$manager->processInvalidations();

清除本地缓存

要仅清除本地缓存而不影响 Redis:

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
$manager->flushLocalCache();

连接管理

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
 
// 检查失效订阅是否已连接
if ($manager->isConnected()) {
// ...
}
 
// 手动断开连接
$manager->disconnect();

优化

工作线程请求限制

Laravel Octane 在可配置的请求数量后循环工作线程,以防止内存泄漏。当工作线程循环时,其本地缓存被清除。增加该限制可以使 Recall 的本地缓存持续更长时间,提高缓存命中率。

在你的 config/octane.php 中:

// 默认是 500 个请求后循环
'max_requests' => 10000,

较高的值意味着更好的缓存利用,但需要有信心你的应用程序没有内存泄漏。在调整该值时监控工作线程的内存使用情况。

带前缀的选择性缓存

使用 cache_prefixes 控制哪些键被本地缓存。这在以下情况中非常宝贵:

// config/recall.php
'cache_prefixes' => [
'users:', // 本地缓存用户数据
'settings:', // 本地缓存应用设置
'products:', // 本地缓存产品目录
],

不匹配这些前缀的键仍然可以使用,但会跳过本地缓存,直接访问 Redis。

内存考虑

APCu 内存

APCu 在所有 PHP 进程之间共享内存。在 php.ini 中配置内存限制:

; 默认是 32MB,增加以满足更大的缓存需求
apc.shm_size = 128M

使用 apcu_cache_info() 监控 APCu 使用情况:

$info = apcu_cache_info();
$memory = $info['mem_size']; // 当前内存使用情况(字节)

Swoole Table 大小

Swoole Tables 在创建时具有固定的容量。根据预期的缓存大小进行计划:

'local_cache' => [
// 最大条目(必须是 2 的幂)
'table_size' => 65536, // 64K 条目
 
// 最大序列化值的字节数
'value_size' => 8192, // 每个值 8KB
],

内存使用情况:table_size × (value_size + overhead)。一个具有 65536 个条目和 8KB 值的表大约使用 512MB。

常见模式

应用配置

// 缓存特性标志和设置
$features = Cache::store('recall')->remember('config:features', 3600, function () {
return Feature::all()->pluck('enabled', 'name')->toArray();
});
 
// 当设置更改时,所有工作线程会自动接收更新

经常访问的参考数据

// 缓存产品类别
$categories = Cache::store('recall')->remember('categories:all', 3600, function () {
return Category::with('children')->whereNull('parent_id')->get();
});
 
// 缓存货币汇率
$rates = Cache::store('recall')->remember('rates:exchange', 300, function () {
return ExchangeRate::all()->pluck('rate', 'currency')->toArray();
});

缓存标签替代

Recall 不支持缓存标签,但你可以通过前缀实现类似的功能:

// 使用一致的前缀代替标签
Cache::store('recall')->put("blog:posts:{$id}", $post, 3600);
Cache::store('recall')->put("blog:comments:{$postId}", $comments, 3600);
 
// 通过前缀清除所有与博客相关的缓存(需要手动实现)
// 或依赖于数据更改时的自动失效

限制

Redis 集群模式

Recall 不支持 Redis 集群模式。CLIENT TRACKING 命令的 REDIRECT 选项要求数据连接和失效订阅连接都在同一 Redis 节点上。在集群中,键根据哈希槽分布在多个节点之间,使得无法接收存储在不同节点上的键的失效通知。

对于集群化 Redis 部署,请考虑:

支持指南

感谢您选择我们的开源包!请花一点时间查看这些支持指南。它们将帮助您充分利用我们的项目。

社区驱动支持

我们的开源项目得益于我们出色的社区。如果您有问题或需要帮助,StackOverflow 和其他在线资源是您最好的选择。

错误和功能优先级

管理一个开源项目的现实是我们不能立即解决每一个报告的错误或功能请求。我们按以下顺序优先处理问题:

1. 影响我们付费产品的错误

影响我们付费产品的错误将始终是我们的首要任务。在某些情况下,我们可能只会处理直接影响我们的错误。

2. 社区拉取请求

如果您发现了一个错误并且有解决方案,请提交拉取请求。在影响我们产品的问题之后,我们给这些社区驱动的修复分配下一个最高优先级。经过审查和批准后,我们将合并您的解决方案并给予贡献者信用。

3. 财务支持

对于不属于上述类别的问题,您可以选择为其解决提供资金支持。每个开放问题都与一个订单表相关联,您可以在上面提供财务支持。我们根据提供的资金金额优先处理这些问题。

社区贡献

开源的蓬勃发展依赖于社区的积极参与。即使您不是在修复错误,也可以通过代码改进、文档更新、教程或在社区频道帮助他人来贡献。我们非常鼓励大家作为一个社区共同支持开源工作。

重申一下,DefectiveCode 将根据错误对我们付费产品的影响、社区拉取请求以及收到的财务支持来优先处理问题.

许可 - MIT 许可

版权所有 © Defective Code, LLC。保留所有权利

特此免费授权任何获得此软件及相关文档文件(“软件”)副本的人,允许在软件中自由处理,包括不限于使用、复制、修改、合并、发布、分发、再授权和/或销售软件副本的权利,并允许被提供软件的人这样做,前提是满足以下条件:

上述版权声明和本许可声明应包括在软件的所有副本或重要部分中。

软件按“原样”提供,不提供任何种类的保证,明示或暗示,包括但不限于对适销性、特定用途适用性和不侵权的保证。在任何情况下,作者或版权持有者均不对因软件或软件的使用或其他交易而引起的任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权或其他方面。