Если вы запускаете приложение Laravel чисто как headless API, то будет выгодно отключить HTTP сессии.
Мы это используем в службе мониторинга Oh Dear, где все удаленные сервера, проверяющие аптайм, это headless API на Laravel.
Дефолтное поведение сессий в Laravel
По дефолту в Laravel включены HTTP-сессии. Это делается через обработчик file
, который их сохраняет в каталог storage/framework/sessions
.
Довольно часто там можно найти тысячи файлов сессий.
$ pwd storage/framework/sessions $ ls -alh | wc -l 1155
Каждый новый посетитель (или бот), открывающий ваше приложение, генерирует новый уникальный идентификатор сессии PHP.
Фактически для наших конечных точек API это складывается.
$ ls -alh | wc -l 33984
Более 30 000 файлов! А ведь мы даже не использовали сессии. И все потому, что они включены по умолчанию!
Как сессии влияют на производительность?
Вы можете хранить сессии в файлах (по дефолту), в Redis, в Memcached,… Но файловый обработчик по умолчанию требует очистки в ручную, иначе файлы старых сессий никогда не истекут.
Это настраивается в файле config/sessions.php
.
/* |-------------------------------------------------------------------------- | Session Sweeping Lottery (Лотерея очистки сессий) |-------------------------------------------------------------------------- | | Некоторые драйверы сессий должны вручную очищать свои хранилища от старых сессий. | Ниже приведены шансы, что это случится по текущему запросу. | По умолчанию, шансы составляют 2 из 100. | */ 'lottery' => [2, 100],
Это означает, что только 2% всех запросов будут запускать код, проверяющий каждый файл в каталоге и удаляющий старые сессии. Это сборщик мусора ваших сессионных файлов.
2% — звучит не так уж и много, правда?
Для большинства приложений это разумное значение. Однако наш API получает несколько миллионов запросов в день, и в этот момент все начинает складываться.
2% всех этих запросов будут в цикле обрабатывать каталог, проверять возраст каждого файла и удаляя его при необходимости. Это означает, что мы имеем (значительное) случайное замедление 2% всех наших вызовов API.
Если вы сделайте strace
такого запроса, то вы увидите, насколько интенсивными могут быть операции с данными.
(Strace показывает системные или низкоуровневые вызовы, которые выполняет процесс php.)
$ strace -p [PID] -e trace=file [...] [pid 12084] lstat("storage/framework/sessions/L0Ecb4JK1yeOVyEZgTmIscT8uuVOfXJE3OI174Rp", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] stat("storage/framework/sessions/L0Ecb4JK1yeOVyEZgTmIscT8uuVOfXJE3OI174Rp", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] access("storage/framework/sessions/L0Ecb4JK1yeOVyEZgTmIscT8uuVOfXJE3OI174Rp", F_OK) = 0 [pid 12084] lstat("storage/framework/sessions/GGjrcIYeceatB0EK9dvpqT4thFQM39PfDQFSbW6R", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] stat("storage/framework/sessions/GGjrcIYeceatB0EK9dvpqT4thFQM39PfDQFSbW6R", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] access("storage/framework/sessions/GGjrcIYeceatB0EK9dvpqT4thFQM39PfDQFSbW6R", F_OK) = 0 [pid 12084] lstat("storage/framework/sessions/TGi8DXmKJfT7p6YFaJ14lnHGpN2ey6ydqBpqodZv", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] stat("storage/framework/sessions/TGi8DXmKJfT7p6YFaJ14lnHGpN2ey6ydqBpqodZv", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] access("storage/framework/sessions/TGi8DXmKJfT7p6YFaJ14lnHGpN2ey6ydqBpqodZv", F_OK) = 0 [pid 12084] lstat("storage/framework/sessions/pvZdw0hYzvn6BdxdTslKeBTuQIeVZj8V2cV7OswA", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] stat("storage/framework/sessions/pvZdw0hYzvn6BdxdTslKeBTuQIeVZj8V2cV7OswA", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] access("storage/framework/sessions/pvZdw0hYzvn6BdxdTslKeBTuQIeVZj8V2cV7OswA", F_OK) = 0 [pid 12084] lstat("storage/framework/sessions/eu19GV2bGUfe1yGviTkuObRYC35mUNOLjQmsDQ3z", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] stat("storage/framework/sessions/eu19GV2bGUfe1yGviTkuObRYC35mUNOLjQmsDQ3z", {st_mode=S_IFREG|0644, st_size=118, ...}) = 0 [pid 12084] access("storage/framework/sessions/eu19GV2bGUfe1yGviTkuObRYC35mUNOLjQmsDQ3z", F_OK) = 0
Для каждого файла сессий из каталога storage/framework/session
необходимо выполнить 3 системных вызова.
Умножьте это на количество имеющихся у вас файлов, и поймете, что PHP потратит приличное количество процессорного времени на их обработку.
Приведенная выше диаграмма CPU взята с одного из наших удаленных серверов проверки аптаймов. Мы развернули фикс, отключающий HTTP-сессии для API примерно в 10:45.
С этого момента нагрузка на нашу систему стала намного более стабильной и контролируемой. Мы избавились от всех случайных пиков работы процессора, происходивших из-за очистки файлов сессий.
Отключение HTTP-сессий в Laravel
Если вам вообще не нужны HTTP-сессии, то отключить их довольно просто. Откройте ваш app/Http/Kernel.php
и удалите следующие мидлвары из группы protected $middlewareGroups
:
\Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class,
После этого ваше приложение больше не будет запускать или загружать сессии.
Почему бы просто не переключиться на {memcached, redis,…}?
Да, memcached или redis имеют собственный механизм очистки, который также предотвратил бы случайные скачки CPU. В конце концов, больше не PHP будет заниматься очисткой.
Но, так как нам вообще не нужны сессии, то я даже не хочу открывать соединение/сокет обработчика сессии для каждого запроса. Намного чище будет просто отключить HTTP-сессии для нашего API.
Если вам все же нужен обработчик файловых сессий…
…возможно, стоит поэкспериментировать, можете ли вы полностью отключить лотерею/сборщик мусора и запустить собственный процесс, который займется этим.
Если вы отключите лотерею и не будете очищать сессии вручную, то они будут накапливаться и в конечном итоге займут всё ваше дисковое пространство. Но отказ от стандартного подхода очистки сессий может быть полезен. Ваши HTTP-запросы больше не вызовут случайные скачки CPU из-за очистки сессий, так как уборкой займутся кастомные воркеры.
Увеличит ли это быстродействие?
Зависит от многих причин.
В нашем случае увеличило — из-за имеющегося огромного количества запросов к нашему API. Если же вы обслуживаете нескольких тысяч запросов к API в день, то, скорее всего, для вас ничего не изменится, или же, в лучшем случае, ускорение будет минимальным.
Автор: Mattias Geniar
Перевод: Алексей Широков
Подписывайтесь на новости в Вконтакте, Твиттер и Телеграмм.
Наш Телеграм-канал — следите за новостями о Laravel.