Laravel 5 проигрывает сеансы и значения конфигурации .env в приложениях с интенсивным использованием AJAX
Я использую Laravel 5 (чтобы быть конкретным, версия laravel/framework "v5.0.27" ), с драйвером session = 'file'.
Я разрабатываю 64-разрядную машину Windows 7.
Я заметил, что иногда (раз в неделю или около того) я неожиданно и случайно выхожу из системы. Иногда это происходит даже сразу после входа в систему. Я добавил сообщения журнала в мой логический код, но код журнала не был запущен. Laravel вел себя так, как будто он полностью потерял файл сеанса.
Еще одна серьезная проблема заключалась в том, что иногда после отладки сессий (с использованием xdebug и Netbeans) Laravel начал проигрывать и другие файлы - настройки .env, некоторые файлы JS debugbar и т.д. В журнале ошибок были такие сообщения, как:
[2015-07-08 13:05:31] local.ERROR: exception 'ErrorException' with message 'mcrypt_encrypt(): Key of size 7 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported' in D:\myproject\vendor\laravel\framework\src\Illuminate\Encryption\Encrypter.php:81
[2015-07-08 13:05:31] local.ERROR: exception 'PDOException' with message 'SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'forge'' in D:\myproject\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:47
Это ясно говорит о том, что файл .env не был прочитан Laravel, поэтому он использует настройки по умолчанию:
'database' => env('DB_DATABASE', 'forge'),
'key' => env('APP_KEY', 'somekey'),
Потеря файлов происходила редко, может быть, раз в месяц или около того, и это всегда происходило после сеансов отладки. Мне всегда приходилось перезапускать Apache, чтобы он снова работал.
Чтобы стресс-тест системы и достоверность воспроизведения вопросов, я использовал быстрый взлом в моем контроллере Angular:
setInterval(function(){
$scope.getGridPagedDataAsync();
}, 500);
Это просто базовый запрос данных от Angular к Laravel.
И это все - теперь я могу воспроизвести проигрывание сессии и потерять .env через 3 минуты или меньше.
Я разработал AJAX-интенсивные веб-приложения ранее на том же ПК с тем же Apache + PHP, но без Laravel, без .env, и я раньше не замечал таких проблем.
Во время отладки через код я узнал, что Laravel не использует встроенные сессии PHP вообще, но реализовал свой собственный сеанс на основе файлов. Очевидно, что он не обеспечивает такую же надежность, как и PHP-сессии по умолчанию, и я не уверен, почему.
Конечно, в реальных сценариях мое приложение не будет таким интенсивным AJAX, но в моих опытах в некоторых случаях достаточно всего двух одновременных запросов AJAX, чтобы потерять сеанс.
Я видел некоторые связанные отчеты об ошибках в Laravel для различных вопросов сессии. Однако я еще ничего не видел о dot-env, но, похоже, он страдает от той же проблемы.
Я предполагаю, что Laravel не использует блокировки файлов и ожидания, поэтому, если файл по какой-либо причине не может быть прочитан (возможно, заблокирован каким-то параллельным процессом или Apache), то Laravel просто отказывается и возвращает все, что может.
Есть ли хорошее решение для этого? Может быть, это специфично для Windows, и проблемы исчезнут на машине Linux?
Любопытно, почему разработчики Laravel (или Symfony) еще не исправили свой файл сеансовых файлов. Я знаю, что блокировка/ожидание замедлит его, но было бы здорово хотя бы иметь возможность включить "надежные сеансы".
Тем временем я попытаюсь выполнить код Laravel и посмотреть, смогу ли я придумать какое-нибудь "быстрое и амбициозное" исправление, но было бы гораздо лучше иметь какое-то надежное и "лучшее" решение.
Обновление о .env
Проблема не связана с блокировкой файлов. Я нашел отчет об ошибке Laravel для проблемы .env, который привел меня к связанному отчету для проекта Dotenv, который, в свою очередь, говорит, что это основная проблема PHP. Меня беспокоит то, что дотенвские разработчики говорят, что Дотенв никогда не предназначался для производства, но Ларавель, похоже, полагается на Дотенва.
В https://github.com/laravel/framework/pull/8187 похоже решение, которое должно работать в одном направлении, но некоторые комментаторы говорят, что в их случае проблема была противоположной. Кто-то, называемый crynobone, дал умный фрагмент кода, чтобы попробовать:
$value = array_get($_ENV, $key, getenv($key));
Появилось другое предложение использовать "makeMutable()" как для Dotenv, так и для Laravel Githubs, но комментаторы сообщают, что это может нарушить тесты.
Итак, я попробовал crynobone, но для меня это не сработало. Во время отладки я узнал, что в случае, когда что-то ломается для одновременных запросов, ключ $не может быть найден ни в getenv(), ни в $_ENV, ни даже в $_SERVER.
Единственное, что сработало (quick & dirty experminet), заключалось в том, чтобы добавить:
static:: $cached [$ name] = $value;
в класс Dotenv, а затем в helpers.php env(), я вижу, что:
Dotenv::$cached[$key]
всегда хорош, даже когда $_ENV и getenv оба ничего не дают.
Хотя Dotenv не предназначался для производства, я не хочу менять рабочий процесс развертывания и конфигурации.
Далее мне придется исследовать проблемы сессии...
Добавление
Связанные отчеты об ошибках Laravel (некоторые даже из версии 4. и, похоже, не исправлены):
https://github.com/laravel/framework/issues/4576
https://github.com/laravel/framework/issues/5416
https://github.com/laravel/framework/issues/8172
и старую статью, которая проливает свет на то, что происходит (по крайней мере, с проблемами сеанса):
http://thwartedefforts.org/2006/11/11/race-conditions-with-ajax-and-php-sessions/
Ответы
Ответ 1
Мое личное мнение, что использование .env для настройки Laravel - плохое решение. Наличие файлов .php, содержащих ключ: значение стиля конфигурации было намного лучше.
Однако проблема, с которой вы сталкиваетесь, - это не ошибка PHP, а Apache - это, скорее всего, проблема с Windows.
Несколько вещей: Apache содержит модуль, который позволяет интегрировать PHP-бинар в процесс или поток Apache, называемый mod_php
- проблема заключается в том, что PHP не только медленный, но и другой бинарный интегрированный в существующий один супер сложно, и вещи могут быть упущены. В этом случае PHP также должен быть построен с обеспечением безопасности потоков. Если это не так, тогда могут возникнуть (и будут) странные ошибки.
Чтобы обойти проблему сложной интеграции одной программы в другую, мы можем полностью ее избежать, и мы можем .php
обслуживать протокол FastCGI. Это означает, что веб-сервер (Apache или Nginx) будет принимать HTTP-запрос и передавать его на другой "веб-сервер". В нашем случае это будет PHP FastCGI Process Manager или PHP-FPM
.
PHP-FPM
является предпочтительным способом обслуживания .php
страниц - не только потому, что он быстрее (гораздо быстрее, чем интеграция через mod_php
), но вы можете легко масштабировать свой интерфейс HTTP и иметь несколько машин, обслуживающих .php
страниц, что позволяет легко горизонтально масштабировать ваш интерфейс HTTP.
Однако PHP-FPM
- это что-то, называемое процессом супервизора, и оно зависит от управления процессом. Насколько мне известно, Windows не поддерживает управление процессами так, как это делает * nix, поэтому PHP-FPM
недоступен для Windows (в случае, если я ошибаюсь, пожалуйста, исправьте меня).
Что все это значит для вас? Это означает, что вы должны использовать программное обеспечение, предназначенное для того, чтобы хорошо играть с тем, что вы хотите сделать.
Это логика, которой следует следовать:
- Веб-сервер принимает HTTP-запросы (Apache или Nginx)
- Веб-сервер проверяет запрос, анализирует необработанный HTTP-запрос, определяет, является ли запрос слишком большим, и если в этом случае все будет хорошо, он проксирует запрос на
PHP-FPM
.
-
PHP-FPM
обрабатывает запрос (в вашем случае он загружает Laravel) и возвращает HTML, который веб-сервер показывает пользователю.
Теперь этот процесс в то время как отличный, имеет несколько проблем, и одна огромная проблема заключается в том, как PHP работает с сеансами. По умолчанию PHP-сессия - это файл, хранящийся где-то на сервере. Это означает, что если у вас есть 2 физических аппарата, обслуживающих ваш PHP-FPM
, у вас будут проблемы с сеансами. Здесь Laravel делает что-то отличное - он позволяет вам использовать зашифрованные сеансы на основе файлов cookie. Он имеет ограничения (вы не можете хранить ресурсы на этих сеансах, и у вас есть ограничение по размеру), но правильно построенное приложение не будет хранить слишком много информации в сеансе в первую очередь. Есть, конечно, несколько способов общения с сеансами, но, на мой взгляд, зашифрованный куки файл супер, супер тривиальный для использования и мощный. Когда такой куки файл используется, клиент, который несет информацию о сеансе и любую машину, которая содержит ключ дешифрования, может прочитать этот сеанс, что означает, что вы можете легко масштабировать свою настройку на нескольких серверах - все, что им нужно сделать, это иметь доступ к тем же ключ дешифрования (это APP_KEY
в .env
). В основном вам необходимо скопировать ту же установку Laravel на машины, которые вы хотите обслуживать своим проектом.
То, как я буду заниматься проблемой, которую вы сейчас разрабатываете, выглядит следующим образом:
- Используйте виртуальную машину (скажем, Oracle Virtualbox)
- Установить Ubuntu 14.04
- Сопоставьте сетевой диск с хост-машиной Windows (используя Samba)
- Используйте ваш предпочтительный способ редактирования файлов PHP, но они будут сохранены на подключенном диске.
- Загрузите nginx или Apache вместе с
PHP-FPM
на виртуальной машине, чтобы обслуживать ваш проект.
Теперь то, что вы получаете через этот процесс, таково: вы не загрязняете свою машину Windows программой, которая слушает на портах 80/443, когда вы закончите работу, вы можете просто закрыть виртуальную машину без потери работы, вы также может легко имитировать, как ваш сайт будет вести себя на реальной производственной машине, и у вас не будет сюрпризов, таких как "он работает на моей машине для разработчиков, но он не работает на моей производственной машине", потому что у вас будет такое же программное обеспечение для обеим целям.
Это мои мнения, они не все холодные факты, и то, что я написал здесь, должно быть принято с солью. Если вы думаете, что то, что я написал, может вам помочь, тогда, пожалуйста, постарайтесь подойти к проблеме таким образом. Если нет, ну, никаких особых чувств, и я желаю вам удачи в ваших проектах.
Ответ 2
После двух дней интенсивной отладки у меня есть некоторые обходные пути, которые могут быть полезны для других:
Вот мой патч для Dotenv 1.1.0 и Laravel 5.0.27 для исправления проблем .env:
https://gist.github.com/progmars/db5b8e2331e8723dd637
И вот мой патч для исправления, чтобы сделать проблемы сеанса гораздо менее частыми (или исправить их полностью, если вы не пишете на сеанс самостоятельно по каждому запросу):
https://gist.github.com/progmars/960b848170ff4ecd580a
Я тестировал их с помощью Laravel 5.0.27 и Dotenv 1.1.0.
Также недавно воссозданные патчи для Laravel 5.1.1 и Dotenv 1.1.1:
https://gist.github.com/progmars/e750f46c8c12e21a10ea
https://gist.github.com/progmars/76598c982179bc335ebb
Убедитесь, что вы добавили
'metadata_update_threshold' => 1,
в ваш config/session.php, чтобы этот патч стал эффективным.
Все патчи должны применяться к папке vendor
каждый раз, когда она воссоздается.
Также вы можете отделить сеанс патча, потому что вы обновляете session.php
только один раз, но остальные части патча должны применяться к папке vendor
каждый раз, когда они воссоздаются перед развертыванием.
Будьте предупреждены: "Это работает на моей машине". Я очень надеюсь, что разработчики Laravel и Dotenv придумают что-то лучшее, но пока я могу жить с этими исправлениями.