spatie/laravel-responsecache

通过缓存整个响应来加速Laravel应用程序


README

Social Card of Laravel Response Cache

通过缓存整个响应来加速应用程序

Latest Version on Packagist MIT Licensed Tests Total Downloads

此Laravel包可以缓存整个响应。默认情况下,它将缓存所有成功返回基于文本内容(如HTML和JSON)的GET请求一周。这可能会显著提高响应速度。

因此,第一次请求时,包将保存响应然后再发送给用户。当相同的请求再次到来时,我们不会通过整个应用程序,而是直接响应已保存的响应。

您是视觉学习者吗?那么请观看这个视频,了解如何使用laravel-responsecache及其内部工作原理。

支持我们

我们投入了大量资源来创建一流的开放源代码包。您可以通过购买我们的付费产品之一来支持我们。

我们非常感激您从您的家乡寄给我们明信片,并说明您正在使用我们哪个包。您可以在我们的联系页面上找到我们的地址。我们将所有收到的明信片发布在我们的虚拟明信片墙上

安装

如果您使用PHP 7,请安装此包的v6.x版本。

您可以通过composer安装此包

composer require spatie/laravel-responsecache

此包将自动注册自己。

您可以使用以下命令发布配置文件

php artisan vendor:publish --tag="responsecache-config"

这是已发布配置文件的内容

// config/responsecache.php

return [
    /*
     * Determine if the response cache middleware should be enabled.
     */
    'enabled' => env('RESPONSE_CACHE_ENABLED', true),

    /*
     *  The given class will determinate if a request should be cached. The
     *  default class will cache all successful GET-requests.
     *
     *  You can provide your own class given that it implements the
     *  CacheProfile interface.
     */
    'cache_profile' => Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests::class,

    /*
     *  Optionally, you can specify a header that will force a cache bypass.
     *  This can be useful to monitor the performance of your application.
     */
    'cache_bypass_header' => [
        'name' => env('CACHE_BYPASS_HEADER_NAME', null),
        'value' => env('CACHE_BYPASS_HEADER_VALUE', null),
    ],

    /*
     * When using the default CacheRequestFilter this setting controls the
     * default number of seconds responses must be cached.
     */
    'cache_lifetime_in_seconds' => env('RESPONSE_CACHE_LIFETIME', 60 * 60 * 24 * 7),

    /*
     * This setting determines if a http header named with the cache time
     * should be added to a cached response. This can be handy when
     * debugging.
     */
    'add_cache_time_header' => env('APP_DEBUG', true),

    /*
     * This setting determines the name of the http header that contains
     * the time at which the response was cached
     */
    'cache_time_header_name' => env('RESPONSE_CACHE_HEADER_NAME', 'laravel-responsecache'),

    /*
     * This setting determines if a http header named with the cache age
     * should be added to a cached response. This can be handy when
     * debugging.
     * ONLY works when "add_cache_time_header" is also active!
     */
    'add_cache_age_header' => env('RESPONSE_CACHE_AGE_HEADER', false),

    /*
     * This setting determines the name of the http header that contains
     * the age of cache
     */
    'cache_age_header_name' => env('RESPONSE_CACHE_AGE_HEADER_NAME', 'laravel-responsecache-age'),

    /*
     * Here you may define the cache store that should be used to store
     * requests. This can be the name of any store that is
     * configured in app/config/cache.php
     */
    'cache_store' => env('RESPONSE_CACHE_DRIVER', 'file'),

    /*
     * Here you may define replacers that dynamically replace content from the response.
     * Each replacer must implement the Replacer interface.
     */
    'replacers' => [
        \Spatie\ResponseCache\Replacers\CsrfTokenReplacer::class,
    ],

    /*
     * If the cache driver you configured supports tags, you may specify a tag name
     * here. All responses will be tagged. When clearing the responsecache only
     * items with that tag will be flushed.
     *
     * You may use a string or an array here.
     */
    'cache_tag' => '',

    /*
     * This class is responsible for generating a hash for a request. This hash
     * is used to look up an cached response.
     */
    'hasher' => \Spatie\ResponseCache\Hasher\DefaultHasher::class,

    /*
     * This class is responsible for serializing responses.
     */
    'serializer' => \Spatie\ResponseCache\Serializers\DefaultSerializer::class,
];

最后,您应该安装提供的中间件 \Spatie\ResponseCache\Middlewares\CacheResponse::class\Spatie\ResponseCache\Middlewares\DoNotCacheResponse

对于laravel 11.x及更高版本

将中间件定义添加到引导应用程序。

// bootstrap/app.php


->withMiddleware(function (Middleware $middleware) {
    ...
    $middleware->web(append: [
        ...
        \Spatie\ResponseCache\Middlewares\CacheResponse::class,
    ]);

    ...

    $middleware->alias([
        ...
        'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class,
    ]);
})

对于laravel 10.x及更早版本

将中间件定义添加到http内核。

// app/Http/Kernel.php

...

protected $middlewareGroups = [
   'web' => [
       ...
       \Spatie\ResponseCache\Middlewares\CacheResponse::class,
   ],

...

protected $middlewareAliases = [
   ...
   'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class,
];

使用方法

基本使用

默认情况下,此包将缓存所有成功的 GET 请求一周。登录用户将各自拥有自己的独立缓存。如果这是您需要的行为,您就完成了:安装 ResponseCacheServiceProvider 就足够了。

清除缓存

手动

可以使用以下命令清除整个缓存

ResponseCache::clear();

这将清除配置文件中指定的缓存存储中的所有内容。

使用控制台命令

可以通过发出此artisan命令实现相同的效果

php artisan responsecache:clear

使用模型事件

您可以利用模型事件在模型保存或删除时清除缓存。以下是一个示例。

namespace App\Traits;

use Spatie\ResponseCache\Facades\ResponseCache;

trait ClearsResponseCache
{
    public static function bootClearsResponseCache()
    {
        self::created(function () {
            ResponseCache::clear();
        });

        self::updated(function () {
            ResponseCache::clear();
        });

        self::deleted(function () {
            ResponseCache::clear();
        });
    }
}

忘记一个或多个特定的URI

您可以使用以下方法忘记特定的URI

// Forget one
ResponseCache::forget('/some-uri');

// Forget several
ResponseCache::forget(['/some-uri', '/other-uri']);

// Equivalent to the example above
ResponseCache::forget('/some-uri', '/other-uri');

当您没有在缓存配置中使用 cacheNameSuffix 时,ResponseCache::forget 方法才有效,使用 ResponseCache::selectCachedItems 来处理 cacheNameSuffix

忘记一组缓存项

您可以使用 ResponseCache::selectCachedItems() 来指定应该忘记哪些缓存项。

// forgetting all PUT responses of /some-uri
ResponseCache::selectCachedItems()->withPutMethod()->forUrls('/some-uri')->forget();

// forgetting all PUT responses of multiple endpoints
ResponseCache::selectCachedItems()->withPutMethod()->forUrls(['/some-uri','/other-uri'])->forget();

// this is equivalent to the example above
ResponseCache::selectCachedItems()->withPutMethod()->forUrls('/some-uri','/other-uri')->forget();

// forget /some-uri cached with "100" suffix (by default suffix is user->id or "")
ResponseCache::selectCachedItems()->usingSuffix('100')->forUrls('/some-uri')->forget();

// all options combined
ResponseCache::selectCachedItems()
    ->withPutMethod()
    ->withHeaders(['foo'=>'bar'])
    ->withCookies(['cookie1' => 'value'])
    ->withParameters(['param1' => 'value'])
    ->withRemoteAddress('127.0.0.1')
    ->usingSuffix('100') 
    ->usingTags('tag1', 'tag2')
    ->forUrls('/some-uri', '/other-uri')
    ->forget();

cacheNameSuffix 依赖于您的缓存配置,默认为用户ID或未认证时的空字符串。

防止请求被缓存

可以通过使用doNotCacheResponse中间件来忽略请求。这个中间件可以被分配给路由和控制器

使用中间件后,我们的路由可以免于缓存。

// app/Http/routes.php

Route::get('/auth/logout', ['middleware' => 'doNotCacheResponse', 'uses' => 'AuthController@getLogout']);

或者,你可以将中间件添加到控制器中

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('doNotCacheResponse', ['only' => ['fooAction', 'barAction']]);
    }
}

故意绕过缓存

你可以故意并安全地绕过缓存,并确保你始终接收到最新响应。这可能在你想要分析某个端点或需要调试响应时很有用。在任何情况下,你只需要填写CACHE_BYPASS_HEADER_NAMECACHE_BYPASS_HEADER_VALUE环境变量,然后在请求时使用该自定义头。

创建自定义缓存配置文件

为了确定哪些请求应该被缓存以及缓存多长时间,使用一个缓存配置类。处理这些问题的默认类是Spatie\ResponseCache\CacheProfiles\CacheAllSuccessfulGetRequests

你可以通过实现 Spatie\ResponseCache\CacheProfiles\CacheProfile接口来创建自己的缓存配置类。让我们看看这个接口

interface CacheProfile
{
    /*
     * Determine if the response cache middleware should be enabled.
     */
    public function enabled(Request $request): bool;

    /*
     * Determine if the given request should be cached.
     */
    public function shouldCacheRequest(Request $request): bool;

    /*
     * Determine if the given response should be cached.
     */
    public function shouldCacheResponse(Response $response): bool;

    /*
     * Return the time when the cache must be invalidated.
     */
    public function cacheRequestUntil(Request $request): DateTime;

    /**
     * Return a string to differentiate this request from others.
     *
     * For example: if you want a different cache per user you could return the id of
     * the logged in user.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return mixed
     */
    public function useCacheNameSuffix(Request $request);
}

缓存特定路由

除了全局注册cacheResponse中间件外,你还可以将其注册为路由中间件。

protected $middlewareAliases = [
   ...
   'cacheResponse' => \Spatie\ResponseCache\Middlewares\CacheResponse::class,
];

在使用路由中间件时,你可以指定这些路由应缓存多少秒

// cache this route for 5 minutes
Route::get('/my-special-snowflake', 'SnowflakeController@index')->middleware('cacheResponse:300');

// cache all these routes for 10 minutes
Route::group(function() {
   Route::get('/another-special-snowflake', 'AnotherSnowflakeController@index');

   Route::get('/yet-another-special-snowflake', 'YetAnotherSnowflakeController@index');
})->middleware('cacheResponse:600');

使用标签

如果你的缓存驱动程序配置支持标签,你可以在应用中间件时指定一系列标签。

// add a "foo" tag to this route with a 300 second lifetime
Route::get('/test1', 'SnowflakeController@index')->middleware('cacheResponse:300,foo');

// add a "bar" tag to this route
Route::get('/test2', 'SnowflakeController@index')->middleware('cacheResponse:bar');

// add both "foo" and "bar" tags to these routes
Route::group(function() {
   Route::get('/test3', 'AnotherSnowflakeController@index');

   Route::get('/test4', 'YetAnotherSnowflakeController@index');
})->middleware('cacheResponse:foo,bar');

清除带标签的内容

你可以清除被分配了标签或标签列表的响应。例如,这个语句将删除上面的'/test3''/test4'路由

ResponseCache::clear(['foo', 'bar']);

相比之下,这个语句只会删除'/test2'路由

ResponseCache::clear(['bar']);

注意,这使用了Laravel的内置缓存标签功能,这意味着路由也可以像通常一样被清除

Cache::tags('special')->flush();

事件

你可以使用几个事件来监视和调试应用程序中的响应缓存。

ResponseCacheHit

Spatie\ResponseCache\Events\ResponseCacheHit

当请求通过ResponseCache中间件并找到并返回缓存的响应时,将触发此事件。

CacheMissed

Spatie\ResponseCache\Events\CacheMissed

当请求通过ResponseCache中间件但没有找到或返回缓存的响应时,将触发此事件。

ClearingResponseCache和ClearedResponseCache

Spatie\ResponseCache\Events\ClearingResponseCache

Spatie\ResponseCache\Events\ClearedResponseCache

当开始和完成responsecache:clear时分别触发这些事件。

创建一个替换器

为了用动态内容替换缓存的 内容,你可以创建一个替换器。默认情况下,我们在配置文件中添加了CsrfTokenReplacer

你可以通过实现Spatie\ResponseCache\Replacers\Replacer接口来创建自己的替换器。让我们看看这个接口

interface Replacer
{
    /*
     * Prepare the initial response before it gets cached.
     *
     * For example: replace a generated csrf_token by '<csrf-token-here>' that you can
     * replace with its dynamic counterpart when the cached response is returned.
     */
    public function prepareResponseToCache(Response $response): void;

    /*
     * Replace any data you want in the cached response before it gets
     * sent to the browser.
     *
     * For example: replace '<csrf-token-here>' by a call to csrf_token()
     */
    public function replaceInCachedResponse(Response $response): void;
}

之后,你可以在responsecache.php配置文件中定义你的替换器

/*
 * Here you may define replacers that dynamically replace content from the response.
 * Each replacer must implement the Replacer interface.
 */
'replacers' => [
    \Spatie\ResponseCache\Replacers\CsrfTokenReplacer::class,
],

自定义序列化器

序列化器负责将响应序列化以便存储在缓存中。它还负责从缓存中重建响应。

默认序列化器Spatie\ResponseCache\Serializer\DefaultSerializer在大多数情况下都能正常工作。

如果你有一些特殊的序列化需求,你可以在配置文件的serializer键中指定自定义序列化器。任何实现了Spatie\ResponseCache\Serializers\Serializer的类都可以使用。这个接口看起来是这样的

namespace Spatie\ResponseCache\Serializers;

use Symfony\Component\HttpFoundation\Response;

interface Serializer
{
    public function serialize(Response $response): string;

    public function unserialize(string $serializedResponse): Response;
}

测试

你可以使用以下命令运行测试

composer test

替代方案

更新日志

请参阅 更新日志 以获取更多最近更改的信息。

贡献

请参阅 贡献指南 了解详细信息。

安全性

如果您发现有关安全性的错误,请通过邮件发送到 [email protected],而不是使用问题跟踪器。

鸣谢

特别感谢 Caneco 为标志 ✨

许可

MIT 许可协议(MIT)。有关更多信息,请参阅 许可文件