Laravel 5 потерянных сеансов и значения конфигурации.env в приложениях, интенсивно использующих AJAX
Я использую Laravel 5 (точнее, версия "laravel/framework" - "v5.0.27"), с драйвером сессии = 'file'.
Я занимаюсь разработкой на Windows 7 64-битной машине.
Я заметил, что иногда (раз в неделю или около того) я неожиданно и случайно выходил из системы. Иногда это происходит даже сразу после входа в систему. Я добавил сообщения журнала в мой логический код аутентификации, но код журнала не был запущен. Laravel вел себя так, как будто он полностью потерял файл сеанса.
Другая, более серьезная проблема, заключалась в том, что иногда после сеансов отладки (с использованием xdebug и Netbeans) Laravel начинал терять и другие файлы - настройки.env, некоторые файлы JS отладочной панели и т. Д. Журнал ошибок содержал такие сообщения:
[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. Меня беспокоит то, что разработчики Dotenv говорят, что Dotenv никогда не предназначался для производства, но Laravel, похоже, полагается на Dotenv.
В https://github.com/laravel/framework/pull/8187 кажется, есть решение, которое должно работать в одном направлении, но некоторые комментаторы говорят, что в их случае проблема была противоположной. Кто-то по имени crynobone дал умный фрагмент кода, чтобы попробовать:
$value = array_get($_ENV, $key, getenv($key));
Появилось другое предложение использовать makeMutable() на Gitubs Dotenv и Laravel, но комментаторы сообщают, что это может нарушить тесты.
Поэтому я попробовал код кринобона, но он у меня не сработал. Во время отладки я обнаружил, что в моем случае, когда для параллельных запросов происходит сбой, ключ $ не может быть найден ни в getenv(), ни в $_ENV, ни даже в $_SERVER. Единственное, что сработало (быстрый и грязный эксперимент), было добавить:
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/
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 придумают что-то лучшее, но пока я могу жить с этими исправлениями.
Мое личное мнение, что использование.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. Он имеет ограничения (вы не можете хранить ресурсы в этих сеансах и у вас есть ограничение по размеру), но правильно построенное приложение не будет хранить слишком много информации во время сеанса. Есть, конечно, несколько способов работы с сессиями, но, на мой взгляд, зашифрованный cookie - это супер, супер тривиально в использовании и мощно. Когда используется такой файл cookie, именно клиент несет информацию о сеансе, и любой компьютер, содержащий ключ дешифрования, может прочитать этот сеанс, что означает, что вы можете легко масштабировать свои настройки на несколько серверов - все, что им нужно сделать, - это иметь доступ к одному и тому же ключ дешифрования (это APP_KEY
в .env
). По сути, вам нужно скопировать ту же установку Laravel на машины, которые вы хотите обслуживать в своем проекте.
Я бы решил, как решить проблему, возникающую у вас при разработке:
- Используйте виртуальную машину (скажем, Oracle Virtualbox)
- Установите Ubuntu 14.04
- Подключите сетевой диск к хост-компьютеру Windows (используя Samba)
- Используйте предпочитаемый способ редактирования файлов PHP, но они будут храниться на подключенном диске
- Загрузите nginx или Apache вместе с
php-fpm
на виртуальной машине для обслуживания вашего проекта
Теперь вы получаете следующее: вы не загрязняете свой компьютер с Windows программой, которая прослушивает порты 80 / 443, когда вы закончите работу, вы можете просто выключить виртуальную машину, не теряя работы, вы также можете легко смоделируйте, как ваш веб-сайт будет вести себя на реальной рабочей машине, и у вас не будет сюрпризов, таких как "он работает на моей машине разработчика, но не работает на моей рабочей машине", потому что у вас будет одинаковое программное обеспечение для обеих целей.
Это мои мнения, они не все холодные факты, и то, что я здесь написал, должно быть воспринято с недоверием. Если вы думаете, что то, что я написал, может вам помочь, тогда, пожалуйста, попробуйте подойти к проблеме таким образом Если нет, то нет никаких обид и я желаю вам удачи в ваших проектах.