Кэширование

Содержание:

Конфигурация

Laravel предосталвляет выразительный, унифицированный API для различных бэкендов кэширования. Конфигурация кэша расположена в файле config/cache.php. В этом файле вы можете указать, какой драйвер кэша вы бы хотели использовать по умолчанию для вашего приложения. Ларавел поддерживает популярные бэкенды кэширования, такие как Memcached и Redis из коробки.

Файл конфигурации также содержит множество других опций, которые описаны внутри файла, поэтому убедитесь в том, что прочитали эти опции. По умолчанию, Laravel настроен использовать драйвер-кэша file, который хранит сериализованные и закэшированные объекты в файловой системе. Для больших приложений, рекомендуется использовать надёжный драйвер такой как Memcached или Redis. Вы можете даже настроить несколько конфигураций кэша для одного и того же драйвера.

Требования к драйверу

База данных

При использовании database кэш-драйвера, вам будет необходимо установить таблицу, содержащую закешированные элементы. Вы можете увидеть пример декларации Schema для таблицы снизу:

Schema::create('cache', function ($table) {
    $table->string('key')->unique();
    $table->text('value');
    $table->integer('expiration');
});
Вы также можете использовать команду Artisan php artisan cache:table, чтобы сгенерировать миграцию с правильной схемой.

Memcached

Для использование драйвера Memcached требуется установить Memcached PECL пакет. Вы можете указать все сервера в файле конфигурации config/cache.php:

'memcached' => [
    [
        'host' => '127.0.0.1',
        'port' => 11211,
        'weight' => 100
    ],
],

Вы также можете установить опцию host к маршруту Unix-сокета. Если вы это сделали, опция порт port должна быть установлена в значение 0:

'memcached' => [
    [
        'host' => '/var/run/memcached/memcached.sock',
        'port' => 0,
        'weight' => 100
    ],
],

Redis

До использования кэша Redis в приложении Ларавел, вам необходимо установить расширение PhpRedis через PECL или установить компонент predis/predis через Composer.

Для большей информации о настройке Redis, обратитесь на страницу документации Ларавел Redis.


Использование кэша

Получение экземпляра кэша

Контракты Illuminate\Contracts\Cache\Factory и Illuminate\Contracts\Cache\Repository обеспечивают доступ к сервисам кэширования Ларавел. Контракт Factory обеспечивает доступ ко всем драйверам кэша определённых для вашего приложения. Контракт Repository представляет типовую реализацию кэш-драйвера для вашего приложения, как это указано в файле конфигурации cache.

Однако, вы можете также использовать фасад Cache, на который мы будем ссылаться на протяжении всего раздела документации. Фасад Cache предоставляет удобный, быстрый доступ к базовым реализациям кэш-контрактов:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Show a list of all users of the application.
     *
     * @return Response
     */
    public function index()
    {
        $value = Cache::get('key');

        //
    }
}

Получение доступа к нескольким хранилищам кэша

Использовануя фасад Cache, вы можете получить доступ к нескольким хранилищам кэша используя метод store. Ключ, пердаваемый в метод store должен соответсвовать одному из перечисленных в массиве конфигурации stores массиву в вашем файле cache:

$value = Cache::store('file')->get('foo');

Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes

Извлечение элементов из кэша

Метод get для фасада Cache используется для получения элементов из кэша. Если таких не существует, вернётся null. Если вы хотите, вы можете передать второй аргумент в метод get, указывающий базовое значение, которое вернётся в случае, если элемент не будет существовать:

$value = Cache::get('key');

$value = Cache::get('key', 'default');

Вы также можете передавать Замыкание в качестве базового значения. Результат замыкания вернётся, если указанный элемент не сущесвует в кэше. Передача в Замыкание позволит вам определить получение базового значение из базы данных или других внешних сервисов:

$value = Cache::get('key', function () {
    return DB::table(...)->get();
});

Проверка на существование элементов

Метод has может быть использован для определения того, что элемент существует в кэше. этот метод вернёт false, если значение равно null:

if (Cache::has('key')) {
    //
}

Увеличение и Уменьшение значений

Методы increment и decrement могут быть использованы для регулирования значения числового элемента в кэше. Оба метода принимают необязательный второй аргумент, который показывает на сколько будет увеличено или уменьшего значение:

Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);

Получение и хранение

Иногда вы можете пожелать получить элемент из кэша, но также сохранить базовое значение, если запрашиваемый элемент не существует. Например, вы можете захотеть получить всех пользователей из кэша, или если их нет, получить из базы данных и добавить в кэш. Вы можете сделать это используя метод Cache::remember:

$value = Cache::remember('users', $seconds, function () {
    return DB::table('users')->get();
});

Если элемент не существует в кэше, замыкани переданное в метод remember будет выполнено, а результат будет размещён в кэше.

Вы также можете использовать метод rememberForever для получения элемента из кэша или сохранить его навсегда:

$value = Cache::rememberForever('users', function () {
    return DB::table('users')->get();
});

Получение и удаление

Если вам нужно получить элемент из кэша и потом удалить элемент, вы можете использовать метод pull. Как и в случае с методом get, если элемента не существует, вернётся null:

$value = Cache::pull('key');

Хранение элементов в кэше

Вы можете использовать метод put для фасада Cache для хранения элементов в кэше:

Cache::put('key', 'value', $seconds);

Если время хранения не передано в метод put, элемент будет храниться на неопределённое время:

Cache::put('key', 'value');

Вместо того, чтобы передавть значение секунд как число, вы можете также передать экземпляр DateTime, который представляет время окончания срока действия кэша:

Cache::put('key', 'value', now()->addMinutes(10));

Сохранение в случае если данных нет

Метод add всего лишь добавит элемент в кэш, если он до сих пор не существует в хранилище. Этот метод вернёт true, если элемента нет в хранилище. Метод вернёт true, если элемент добавлен в кэш. В противном случае, метод вернёт false:

Cache::add('key', 'value', $seconds);

Хранение элементов на всегда

Метод forever может быть использован для перманентного хранения элементов в кэше. В силу того, что такие элементы не пропадают, их нужно удялять вручную, используя метод forget:

Cache::forever('key', 'value');
Если вы используете драйвер Memcached, все элементы, который хранятся "вечно" будут удалены при достижении лимита размера.

Удаление элементов из кэша

Вы можете убарть элементы из кэша используя метод forget:

Cache::forget('key');

Вы также можете убрать элементы путём указания нуля или отрацательного значения времени в качестве 3-го параметра:

Cache::put('key', 'value', 0);

Cache::put('key', 'value', -5);

Вы можете очистить весь кэш используя метод flush:

Cache::flush();
Очистка кэша таким способом удалит весь кэш вне зависимости от префиксов. Нужно быть особенно осторожным при удалении кэша достпного для нескольких приложений.

Атомарные замки

Чтобы использовать эту возможность, ваше приложение должно использовать драйверы memcached, dynamodb, или redis в качестве вашего базового драйвера кэша. В дополнение, все серверы должны взаимодействовать с одним и тем же центральным сервером кэша.

Атомарные замки позволяют управлять блокировками без беспокойства об условиях потока. Например, Laravel Forge используем это решение для обеспечения выполонения только одной задачи на сервере за указанный промежуток времени. Вы можете создавать замки используя метод Cache::lock:

use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('foo', 10);

if ($lock->get()) {
    // Lock acquired for 10 seconds...

    $lock->release();
}

Метод get также принимает Замыкание. После выполнения Замыкания, Ларавел автоматически запустит замок:

Cache::lock('foo')->get(function () {
    // Lock acquired indefinitely and automatically released...
});

Если замок не доступен в момент запроса, вы можете научить Laravel подождать определённое количестве секунд. Если замок не может быть получен за указанный промежуток времение, будет вызвано исключение Illuminate\Contracts\Cache\LockTimeoutException:

use Illuminate\Contracts\Cache\LockTimeoutException;

$lock = Cache::lock('foo', 10);

try {
    $lock->block(5);

    // Lock acquired after waiting maximum of 5 seconds...
} catch (LockTimeoutException $e) {
    // Unable to acquire lock...
} finally {
    optional($lock)->release();
}

Cache::lock('foo', 10)->block(5, function () {
    // Lock acquired after waiting maximum of 5 seconds...
});

Управление блокировками между процессами

Иногда, вы можете захотеть включить блокировку в одном процессе, а снять её в другом. Например, вы можете наложить блокировку во время выполнения веб-запроса, а хотите снять замок после выполнения очередного задания, которое было запущено запросам. В этом сценарии, вы должны передать "токен владельца" в выполняемое задание так, чтобы можно было пересоздать экземпляр блокировки используя данный токен:

// Within Controller...
$podcast = Podcast::find($id);

$lock = Cache::lock('foo', 120);

if ($result = $lock->get()) {
    ProcessPodcast::dispatch($podcast, $lock->owner());
}

// Within ProcessPodcast Job...
Cache::restoreLock('foo', $this->owner)->release();

Если вы хотите запустить блокировщик вне зависимости от текущего владельца, вы можете использовать метод forceRelease:

Cache::lock('foo')->forceRelease();

Помощники

Дополнительно к использованию фасада Cache или контракта cache contract, вы также можете использовать функцию cache для получения и хранения данных в кэше. Когда функция cache вызывается с простым строковым аргументом, он вернёт значение данного ключа:

$value = cache('key');

Если вы предоставляете массив пар ключ / значение и вреия жизни в функцию, оно сохранит значения в кэше на указанный срок:

cache(['key' => 'value'], $seconds);

cache(['key' => 'value'], now()->addMinutes(10));

При вызове функции cache без аргументов, будет возвращён экземпляр реализации Illuminate\Contracts\Cache\Factory, который позволит вам выполнять любые другие методы кэширования:

cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});
При тестировании вызова глобальной функции cache, вы можете использовать метод Cache::shouldReceive так, словно вы тестируете фасад.

Кэширование тэгов

Кэширование тэгов не поддерживается при использовании кэш-драйверов file или database. Кроме того, при использовании кэша, который хранится "вечно", лучше всего использовать драйвер memcached, у которого есть механизм автоматического удаления устаревших записей кэша.

Хранение элементов с тегами

Кэш-тэги позволяют вам помечать связанные элементы в кэше и после очищать все закешированные значения, которые были приписаны к данному тэгу. Вы можете получить доступ к закэшированным тэгам путём передачи массива имён тэгов. Например, давайте получим доступ к тэгам и положим методом put значения в кэш:

Cache::tags(['people', 'artists'])->put('John', $john, $seconds);

Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);

Получение доступа к элемантам с тегами

Для получения тэгов, передайте список тэгов в таком же порядке в метод tags, после чего вызовите метод get с ключом, который вы желаете получить:

$john = Cache::tags(['people', 'artists'])->get('John');

$anne = Cache::tags(['people', 'authors'])->get('Anne');

Удаление элементов с тегами из кэша

Вы можете очистить все элементы, которые приписаны к тэгу или списку тэгов. Например, это выражение удалит все закешированные тэги с people, authors, или двумя. Поэтому, оба Anne и John будут удалены из кэша:

Cache::tags(['people', 'authors'])->flush();

Напротив, это выражение удалит только закешированные тэги с authors, поэтому Anne будет удалена, а John нет:

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

Добавление пользовательского кэш-драйвера

Написание драйвера

Для создания вашего пользовательского кэш-драйвера, мы вначале нужно расширить контракт Illuminate\Contracts\Cache\Store. Поэтому, реализация кэша MongoDB будет выглядеть таким образом:

<?php

namespace App\Extensions;

use Illuminate\Contracts\Cache\Store;

class MongoStore implements Store
{
    public function get($key) {}
    public function many(array $keys) {}
    public function put($key, $value, $seconds) {}
    public function putMany(array $values, $seconds) {}
    public function increment($key, $value = 1) {}
    public function decrement($key, $value = 1) {}
    public function forever($key, $value) {}
    public function forget($key) {}
    public function flush() {}
    public function getPrefix() {}
}

Нам всего лишь нужно расширить эти два метода используя соединение MongoDB. Например, как расширить каждый из этих методов, давайте взглянём на Illuminate\Cache\MemcachedStore в коде фреймворка. После того, как наша регистрация завершена, мы можем завершать нашу регистрацию пользовательского драйвера.

Cache::extend('mongo', function ($app) {
    return Cache::repository(new MongoStore);
});
Если вас интересует куда нужно положить ваш код для пользовательского драйвера кэша, вы можете создать пространство имён Extensions внутри вашей директории app. Однако, вы должны понимать, что Ларавел не содержит жесткой структуры и вы можете свободно организовать структуру приложения согласно вашим предпочтениям.

Регистрация драйвера

Для регистрации кэш-драйвера Laravel, мы будем использовать метод extend для фасада Cache. Вызов Cache::extend должен быть реализован в методе boot базового App\Providers\AppServiceProvider, который включен в типовую установку Laravel, или вы можете создать ваш собественный сервис-провайдер для размещения расширения. Но не забудьте зарегистрировать провайдер в массиве config/app.php:

<?php

namespace App\Providers;

use App\Extensions\MongoStore;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;

class CacheServiceProvider extends ServiceProvider
{
    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Cache::extend('mongo', function ($app) {
            return Cache::repository(new MongoStore);
        });
    }
}

Первый аргумент, который передаётся в метод extend — это имя драйвера. Оно должно соответствовать опции driver в файле конфигурации config/cache.php. Второй аргумент это замыкание, которое должно вернуть экземпляр Illuminate\Cache\Repository. Экземпляр $app, который являеется экзмепляром сервис-контейнера, будет передан в Замыкание.

После того, как ваше расширение было зарегистрировано, обновите опцию driver в вашем файле конфигурации config/cache.php в соответсвии с названием вашего расширения.


События

Для выполнения кода для каждой операции кэша, вы можете прослушивать события, которые запускаются кэшем. Обычно, вы должны разместить слушателей события внутри вашего EventServiceProvider:

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    'Illuminate\Cache\Events\CacheHit' => [
        'App\Listeners\LogCacheHit',
    ],

    'Illuminate\Cache\Events\CacheMissed' => [
        'App\Listeners\LogCacheMissed',
    ],

    'Illuminate\Cache\Events\KeyForgotten' => [
        'App\Listeners\LogKeyForgotten',
    ],

    'Illuminate\Cache\Events\KeyWritten' => [
        'App\Listeners\LogKeyWritten',
    ],
];