Кэширование
Содержание:
Конфигурация
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',
],
];