Для еще более быстрого роста в приложениях Laravel с высоким трафиком вы можете кэшировать аутентифицированных пользователей, чтобы избежать обращения к базе данных.
В этой статье я покажу вам, как это сделать, но это не быстрое решение; Нам придется рассмотреть, что происходит, когда пользователь обновляется или удаляется.
Давайте кэшируем.
Предпочитаете смотреть? Эта статья доступна в виде бесплатного видео на Codecourse!
Фон
Для каждой аутентифицированной страницы или запроса API в вашем приложении Laravel будет извлекать нового пользователя из базы данных с запросом, похожим на этот (в зависимости от идентификатора, конечно):
select * from `users` where `id` = 1 limit 1
В настоящее время нет способа автоматически кэшировать этот объект пользователя. Таким образом, пока ваш пользователь аутентифицирован, этот запрос всегда будет выполняться для каждого запроса.
Поскольку маловероятно, что ваш пользователь будет так часто меняться между запросами, имеет смысл кэшировать его до тех пор, пока что-то не изменится, особенно для приложений с высоким трафиком.
Как работают провайдеры аутентификации в Laravel
Первый шаг — понять механизм провайдера Auth Laravel.
По умолчанию Laravel использует команду для управления аутентифицированными EloquentUserProvider пользователями. Этот класс содержит множество полезных методов, таких как retrieveById, rehashPasswordIfRequired и validateCredentials. В общем, все, что требуется для получения и обновления пользователя в отношении аутентификации.
Вы можете добавить нового провайдера с помощью следующего методаAuth::provider:
Auth::provider('someCustomProvider', function (Application $app, array $config) {
// return a custom provider here
});
После того, как вы добавили этот провайдер во время выполнения, вы можете поменять его местами в файле config/auth.php конфигурации:
'providers' => [
'users' => [
'driver' => 'someCustomProvider',
'model' => env('AUTH_MODEL', App\Models\User::class),
],
Создание пользовательского провайдера аутентификации
Теперь мы немного лучше понимаем провайдеров аутентификации, давайте создадим своих!
Начните с создания класса поставщика, CachedEloquentUserProvider. Вы можете назвать это как угодно или разместить его в любом месте вашего приложения:Это расширяет базуEloquentUserProvider,
namespace App\Auth\Providers;
use Illuminate\Auth\EloquentUserProvider;
class CachedEloquentUserProvider extends EloquentUserProvider
{
public function retrieveById($identifier)
{
//
}
}
чтобы обеспечить всю дополнительную функциональность, которую мы хотим сохранить. Нас интересует только переопределение способа retrieveById выбора способа получения пользователя.
Сейчас мы ничего не делаем для получения пользователя (мы рассмотрим это далее), но давайте подключим его к нашему пользовательскому провайдеру в методе AppServerProvider':
public function boot(): void
{
Auth::provider('cachedEloquent', function (Application $app, array $config) {
return new CachedEloquentUserProvider(
$app['hash'],
$config['model']
);
});
}
В конструкторе для оригинала EloquentUserProviderнам нужно передать текущий hasher (отвечающий за хеширование паролей и т.д.), а также пространство имен модели из config, которое представляет пользователя (обычно App\Models\User).
bootВот почему мы рассмотрели эти две вещи выше.
Теперь переключитесь на драйвер config/auth.php cachedEloquent (или как вы его назвали).
'providers' => [
'users' => [
'driver' => 'cachedEloquent',
'model' => env('AUTH_MODEL', App\Models\User::class),
],
На этом этапе ваше приложение не сможет получить аутентифицированных пользователей, так как мы оставили новый retrieveById метод пустым.
Возврат кэшированного объекта пользователя
Теперь пришло время заполнить retrieveById метод кэшированной версией пользователя.
Вариантов здесь бесконечное множество, но вот хорошее начало:
public function retrieveById($identifier)
{
return cache()->remember('user_' . $identifier, now()->addHours(2), function () use ($identifier) {
return parent::retrieveById($identifier);
});
}
здесь $identifier просто идентификатор пользователя, поэтому мы передаем его родительскому retrieveById методу для выполнения своей работы. Но, конечно, мы используем cache()->remember для кэширования и возврата результата.
Вот немного более короткий способ получить тот же результат с помощью стрелочной функции PHP:
public function retrieveById($identifier)
{
return cache()->remember(
'user_' . $identifier,
now()->addHours(2),
fn () => parent::retrieveById($identifier)
);
}
Любой метод работает нормально; это зависит от того, делаете ли вы что-то еще в замыкании, и в этом случае вы выберете стандартную функцию обратного вызова.
Смените драйвер кэша!
По умолчанию Laravel использует базу данных в качестве драйвера кэша. Вы захотите изменить это, иначе мы вернемся к получению кэшированной версии пользователя из базы данных... снова.
CACHE_STORE=redis
Как только это изменится, вы сможете войти в свое приложение и увидеть первоначальный запрос базы данных для пользователя. Однако при обновлении мы извлекаем данные пользователя из нашего (в данном случае Redis) кэша!
Взлом кэша, когда что-то меняется
Кэширование — это здорово, но нам нужно позаботиться о том, чтобы обезвредить (аннулировать) кэш, когда что-то меняется.
Если пользователь обновит свои данные в приложении прямо сейчас, он не увидит эти изменения немедленно, и ему придется ждать истечения срока действия кэша. Не идеально.
Чтобы обойти эту проблему, мы можем просто наблюдать за изменениями User for и аннулировать кэш вручную.
Начните с создания события UserObserver:
php artisan make:observer UserObserver
Open it up and register для updated и deleted.
class UserObserver
{
public function updated(User $user)
{
cache()->forget('user_' . $user->id);
}
public function deleted(User $user)
{
cache()->forget('user_' . $user->id);
}
}
Наш ключ кэша был установлен в , user_[id]поэтому мы просто аннулируем этот ключ.
Зарегистрируйте наблюдателя на User модели, и все готово:
use App\Observers\UserObserver;
#[ObservedBy(UserObserver::class)]
class User extends Authenticatable
{
//...
}
Когда пользователи меняются или удаляются, кэш становится недействительным, и мы в конечном итоге кэшируем новые данные (или просто полностью удаляем их, если пользователь удален
).Кеширование — это сложно
Важно отметить, что это очень упрощенный подход к аннулированию кэшированных данных пользователя с помощью двух событий Eloquent. На самом деле, по мере усложнения приложения могут возникать крайние случаи, когда вы либо не хотите аннулировать кэш, либо кэш не будет аннулирован в нужное время.
Например:
- Если вы используете
DBфасад в любом месте приложения для обновления пользователя, кэш не будет аннулирован, потому что событие Eloquent не будет запущено - Если данные в базе данных обновляются вручную, кэш не будет аннулирован
- Если непубличный столбец (например, email_verified_at) для вашего пользователя будет обновлен, это сделает кэш недействительным, но вы можете этого не захотеть.
- Например, если у вас есть задание в очереди, которое регулярно обновляет информацию о пользователе (например, когда он в последний раз делал запрос), это приведет к постоянному аннулированию кэша.
Как я уже говорил в начале статьи, это не быстрое и простое решение, которое не требует размышлений в дальнейшем.
Да, это ускорит работу вашего приложения. Тем не менее, с вашей стороны потребуется немного больше усилий, чтобы убедиться, что кэш не возвращает устаревшие данные или вы не делаете кэш недействительным слишком часто и не делаете аутентифицированное кэширование бесполезным.