matchory / response-cache
一个用于为Laravel API添加自动响应缓存的包。
Requires
- php: >=8.2
- illuminate/support: ^v10.48|^11.11
Requires (Dev)
- illuminate/auth: ^v10.48|^11.11
- illuminate/cache: ^v10.48|^11.11
- illuminate/contracts: ^v10.48|^11.11
- illuminate/http: ^v10.48|^11.11
- phpunit/phpunit: ^10.5|^11.2
README
一个为Laravel添加智能缓存的包
使用此包,您可以通过添加中间件来简单地缓存从控制器返回的完整响应实例。
这可以极大地加快您的应用程序速度!
功能
- 易于使用:只需将
CacheResponse添加到您的路由即可! - 完全可定制:缓存可能默认即可使用,但如果需要,您也可以控制缓存的各个方面。
- 支持认证请求:默认情况下,缓存条目将针对每个已认证的用户唯一。
- 在标签中解析路由绑定:缓存标签可以使用路由或请求数据中的任何值动态使用。
Route::get('/example')->middleware([ 'cacheResponse:3600,examples' ])
替代方案
巧合的是,我刚刚了解到 spatie 已经构建了一个几乎与我们完全相同的库
spatie/laravel-responsecache.
为什么不使用Varnish呢?
与外部缓存相比,将响应缓存放在您的应用程序中,您将拥有评估路由绑定、认证状态以及轻松程序化缓存刷新的权力。
这也使得将缓存检查放在中间件堆栈中的任何位置成为可能!
要求
- PHP 8.0或更高版本
- Laravel 7.x或更高版本
安装
使用Composer安装
composer require matchory/response-cache
除非您已禁用包发现,否则一切应该已经设置好了。否则,将服务提供者和外观添加到您的配置中。
可选地,您可以发布配置文件
php artisan vendor:publish --provider "Matchory\ResponseCache\ResponseCacheProvider"
查看配置部分以了解所有可用选项。
将中间件添加到您的 app/Http/Kernel.php
protected $middlewareGroups = [ 'web' => [ // ... \Matchory\ResponseCache\Http\Middleware\CacheResponse::class, // ... ], ]; protected $routeMiddleware = [ // ... 'bypass_cache' => \Matchory\ResponseCache\Http\Middleware\BypassCache::class, // ... ];
配置
配置文件包含以下设置
enabled(环境变量:RESPONSE_CACHE_ENABLED)
使用此设置,您可以为所有请求全局启用或禁用响应缓存。如果您将其设置为 false,则不会缓存任何内容。
默认为 true。
ttl(环境变量:RESPONSE_CACHE_TTL)
此设置指定响应在标记为过时之前将被缓存的时间。值必须以秒为单位指定。
默认为 60 * 60 * 24,即24小时。
store(环境变量:RESPONSE_CACHE_STORE)
在这里,您可以传递在您的 config/cache.php 文件中定义的任何缓存存储。请注意,并非所有存储都支持标签:为了更好地控制和提高缓存性能,我们强烈建议使用具有标签支持的存储,如Redis或APC。
默认为 file。
tags(环境变量:RESPONSE_CACHE_TAGS)
如果您的存储支持标签,则默认情况下,所有缓存的响应也将带有这些标签。根据中间件和策略,还可以使用其他标签。
默认为 []。
server_timing(环境变量:RESPONSE_CACHE_SERVER_TIMING)
如果启用服务器定时选项,则将为所有缓存的响应添加包含初始缓存时间的 Server-Timing 标头。这使得调试更容易。在生产环境中保留启用状态可能除了响应大小增加导致的轻微性能损失外,没有其他负面影响。
默认为 false。
使用
按照上述安装说明操作后,您应该已经准备好:状态码在200和300范围内的响应将在后续访问时缓存并返回。要绕过特定路由的缓存,您可以简单地添加bypass_cache中间件
Route::get('/kitten')->middleware('bypass_cache');
手动清除缓存
要手动清除缓存,您可以使用response-cache:flush artisan命令
php artisan response-cache:flush
这将清除整个响应缓存。要删除特定的缓存标签集合,将它们添加到命令中
php artisan response-cache:flush tag-1 tag-2 ... tag-n
以编程方式清除缓存
在生产环境中,如果在发生某些情况时自动清除缓存通常很有帮助。例如,您可以通过设置观察者来在模型事件触发时清除缓存
use Matchory\ResponseCache\Facades\ResponseCache; class ExampleObserver { public function saved(): void { ResponseCache::clear(); } public function deleted(): void { // By passing one or more tags, only those items tagged appropriately // will be cleared ResponseCache::clear(['example']); } }
通过调用clear方法,将清除给定标签的整个存储。为了更精确,请使用delete方法
use Matchory\ResponseCache\Facades\ResponseCache; ResponseCache::delete('/example/url'); ResponseCache::delete(['/example/1', '/example/2']);
注意: 这对需要上下文信息的缓存项不起作用,例如用户身份验证。如果遇到这个问题,您可能想使用标签。
缓存标签
缓存标签是一种简单而强大的机制,用于高效地使用缓存。通过对一组缓存项应用标签,您可以清除所有这些项,而无需知道这些项的个别缓存键。例如,有许多与“航班”相关的缓存数据点:飞机时刻表、到达日期、乘客记录等。一切正常,直到航班被取消!现在,我们需要清除包含相关航班的任何缓存。如果没有设置缓存标签,这将是一个逻辑噩梦:我们不是手动删除大量项目,而是指示Laravel清除所有标记为航班号的项,然后我们就可以继续了!
这个库在很大程度上是基于缓存标签的概念构建的:通过正确标记,您可以轻松地实现粒度化的缓存清除。可以使用多种方法将标签添加到路由中
- 通过将它们传递给中间件
Route::get('/foo')->middleware(['cache:tag-1,tag-2']);
- 通过将它们添加到
config/response-cache.php配置文件中'tags' => env('RESPONSE_CACHE_TAGS', [ 'tag-1', 'tag-2' ]),
- 通过从您的策略中返回它们。
在缓存标签中使用绑定
此库支持添加基于路由绑定的动态标签。这与普通路由绑定几乎相同,但额外的好处是可以访问所有请求值,而不仅仅是路由本身中提到的值。
要将绑定添加到您的缓存标签中,只需在花括号中包含参数的名称:flights.{flight}。只要要从缓存中检索或放入某些内容,就会使用当前请求实例解析此绑定。如果flight参数是Eloquent模型的实例,它将替换为其路由键名称,因此很可能是航班ID!
请看以下示例
Route::get('/api/v{version}/flights/{flight}', function(\App\Flight $flight) { // ... })->middleware('cache:api.v{version},flights,flights.v{version},flights.{flight},flights.v{version}.{flight}');
假设我们执行以下请求:/api/v3/flights/505
现在,此路由生成的响应将带有以下标签
api.v3flightsflights.v3flights.505flights.v3.505
根据您的需求,您可以简单地清除这些标签中的任何一个,并确信响应将被失效!
缓存策略
一个策略是响应缓存用来确定响应应该如何缓存的。它生成缓存键,确定缓存标签并控制缓存绕过。默认策略应该适用于大多数用例,但如果它不适用,我们也有解决方案!
要使用自定义策略,首先可以扩展默认实现(推荐)或实现CacheStrategy接口。
自定义缓存键
为了自定义缓存键的生成方式,您有多种选择,因为默认实现将此过程拆分为多个方法
use Illuminate\Http\Request;use Matchory\ResponseCache\Support\BaseStrategy; class MyStrategy extends BaseStrategy { public function key(Request $request): string { $identifier = $this->extractRequestIdentifier($request); $suffix = $this->buildSuffix($request); return $this->hash($identifier . $suffix); } }
extractRequestIdentifier方法提取完整的请求 URL 和方法作为缓存键的基础。这对于大多数应用程序来说应该足够了。buildSuffix方法检查当前的身份验证状态,并附加已认证用户的 ID。您可能希望修改它以使用客户或应用程序标识符,例如。hash方法构建给定缓存键的哈希值(默认情况下使用md5),因此键长度保持一致。
自定义缓存绕过
要自定义响应是否缓存,您可以实现一个或多个辅助器
use Illuminate\Http\Request; use Matchory\ResponseCache\Support\BaseStrategy; use Symfony\Component\HttpFoundation\Response; class MyStrategy extends BaseStrategy { public function shouldCache(Request $request, Response $response): bool { if (! $this->isMethodCachable($request)) { return false; } return $this->isSuccessful($response); } }
默认情况下,这将缓存任何使用 GET 或 HEAD 方法的请求,以及具有成功或重定向状态的响应。
自定义标记
标记缓存响应是一个非常强大的功能,允许您在发生某些情况时刷新一组缓存条目。因此,如果集合的某个成员被删除,例如,您可以删除同一集合的所有缓存实例,而无需知道检索它们的精确缓存键!
为了使其尽可能简单,策略提供了一种从请求和响应中提取标记的方法
use Illuminate\Http\Request; use Matchory\ResponseCache\Support\BaseStrategy; use Symfony\Component\HttpFoundation\Response; class MyStrategy extends BaseStrategy { public function tags(Request $request, Response|null $response = null): array { return [ $request->attributes->get('customer.id') ]; } }
手动使用 ResponseCache 中间件
除了像上面那样将缓存中间件添加到所有 Web 路由外,您当然也可以手动将其添加到所选路由。在这种情况下,您还可以为每个路由配置离开时间和添加一组标记
// Default settings as set in the config file Route::get('/foo')->middleware('response_cache') // TTL of 60 seconds Route::get('/bar')->middleware('response_cache:60'); // Tags "foo", "bar" and "baz" are added Route::get('/baz')->middleware('response_cache:foo,bar,baz'); // TTL of 60 seconds and tags "foo", "bar" and "baz" are added Route::get('/quz')->middleware('response_cache:60,foo,bar,baz');
如果中间件的第一个参数是数字,它将被解释为 TTL 值,所有随后的内容都作为标记。TTL 将覆盖 配置值,标记将合并。
对缓存事件做出反应
此库公开了一些您可以监听并相应采取行动的事件。这可能在进行 调试 时最有用。
命中
如果响应在缓存中找到,则发出。包括请求实例。
未命中
如果响应在缓存中找不到,或者被 缓存策略指示为不可缓存,因此必须由应用程序生成,则发出。包括请求实例。
刷新
如果缓存被刷新,则发出。包括被刷新的标记,如果刷新了所有标记,则为 null。
手动响应序列化
默认情况下,响应缓存使用一个非常简单的序列化方法的缓存存储实现:不执行任何操作。任何序列化都推迟到 Laravel 的缓存实现。在罕见的情况下,您可能需要更改此行为并在序列化之前或之后修改响应。
为此,首先扩展 Repository 类
use Matchory\ResponseCache\Repository; use Symfony\Component\HttpFoundation\Response; class CustomRepository extends Repository { protected function serialize(Response $response) : mixed { return serialize($response); } protected function hydrate(mixed $responseData): Response { return unserialize($responseData, [Response::class]) } }
在您的 AppServiceProvider 中覆盖容器绑定,以便使用您的存储库而不是默认存储库
use Matchory\ResponseCache\Repository; protected function register():void { $this->app->bind(Repository::class, CustomRepository::class); }
调试
缓存问题可能会在调试时有点麻烦。此库内置了几个功能,以使过程尽可能简单
- 启用
Server-Timing标头:通过开启此功能,所有响应都将包括一个包含您的响应被缓存时间的Server-Timing标头。此信息将自动显示在您浏览器的开发者工具中! - 监听缓存事件:通过监听 缓存事件,您可以确保一切按预期工作。
- 临时或条件性禁用缓存:通过更改
response-cache.enabled设置,您可以快速确定缓存是否是问题所在。 - 使用自定义仓库:覆盖内置序列化方法以控制响应的序列化和恢复。