PHP-сервер activerecord mysql ушел

Я использую php-activerecord в течение короткого времени, и я абсолютно люблю его. Php-activerecord - это библиотека ORM с открытым исходным кодом, основанная на шаблоне ActiveRecord. Тем не менее, я недавно пытался использовать его в сочетании с приложением websocket на основе Wrench.

Это работает отлично, но для запуска script приложение должно запускаться как демон на linux, чтобы веб-камеры всегда были доступны. Через некоторое время, не используя приложение, а затем попытавшись использовать его снова, он выдает некоторые исключения для базы данных:

Сначала он выдает предупреждение:

PHP Warning: Error while sending QUERY packet. PID=XXXXX in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php on line 322

Затем он выдает фатальную ошибку:

PHP Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php:322

Трассировка стека:

#0 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php(322): PDOStatement->execute(Array)

#1 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(218): ActiveRecord\Connection->query('SELECT * FROM ...', Array)

#2 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(209): ActiveRecord\Table->find_by_sql('SELECT * FROM `...', Array, false, NULL)

#3 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Model.php(1567): ActiveRecord\Table->find(Array)

#4 in /home/user/domains/example.com/public_html/vendor/php-activerecord/lib/Connection.php on line 325

Похоже, что php-activerecord поддерживает соединение mysql все время, которое запускает сервер websocket, это не должно быть проблемой, если он автоматически попытается снова подключиться и запустить запрос. Но это не так.

Я прочитал кое-что о настройке MYSQL_OPT_RECONNECT. Но я не уверен, что это работает или как установить этот параметр с помощью php-activerecord. Кто-нибудь здесь имеет некоторый опыт в этой области?

Изменить: Вот мои глобальные переменные конфигурации тайм-аута

VARIABLE_NAME                   VARIABLE_VALUE  
DELAYED_INSERT_TIMEOUT          300
WAIT_TIMEOUT                    28800
CONNECT_TIMEOUT                 10
LOCK_WAIT_TIMEOUT               31536000
INNODB_ROLLBACK_ON_TIMEOUT      OFF
THREAD_POOL_IDLE_TIMEOUT        60
NET_WRITE_TIMEOUT               60
INNODB_LOCK_WAIT_TIMEOUT        50
INTERACTIVE_TIMEOUT             28800
DEADLOCK_TIMEOUT_LONG           50000000
SLAVE_NET_TIMEOUT               3600
DEADLOCK_TIMEOUT_SHORT          10000
NET_READ_TIMEOUT                30

Ответ 1

PHP ActiveRecord использует PDO. Нет абсолютно никакого способа закрыть PDO-соединение, это неправильный уровень БД для длительных фоновых задач.

Вы можете попытаться повлиять на разъединение PDO-соединения со следующим фрагментом.

//if not using ZF2 libraries, disconnect in some other way
$db->getDriver()->getConnection()->disconnect()
$db = NULL;
gc_collect_cycles();

Отключите, установите ссылку на значение null, затем запустите сборщик мусора. Надеемся, что это вызовет внутренний метод __destruct PDO для фактического закрытия соединения.

Вы должны управлять своими связями с БД в своем собственном длинном script. Вы должны отключиться, если вашему работнику не приходилось обрабатывать работу, и вы должны снова подключиться, когда у вас есть работа.

Реальное решение состоит в том, чтобы не использовать PDO и отключать и повторно подключаться нормально.

Если вы просто устанавливаете тайм-ауты как для сервера, так и для клиентской библиотеки, чтобы быть бесконечным, вы столкнулись с проблемами с неконтролируемыми сценариями, которые никогда не умирают, заставляя вас перезагружать весь сервер (не рекомендуется смешивать таймауты).

EDIT: У меня действительно была эта точная проблема, и я использовал это точное решение на работе в прошлом году. Это позволило решить 99% моих проблем. Но все же время от времени случалось исключение, связанное с блуждающим соединением, которое я не мог поймать и попытаться снова подключиться. Я просто перезапускаю процессы один раз в день, чтобы избавиться от этих ошибок при случайном подключении. Вот почему мой ответ: не используйте PDO. Переключите сейчас и получите реальный контроль над разъединениями и повторным подключением.

Ответ 2

Самая распространенная причина для сервера MySQL - ошибка в том, что время ожидания сервера и закрытие соединения.

Попробуйте выполнить следующее изменение.

max_allowed_packet=64M

Если у вас есть много запросов, установите это и не устанавливайте его больше, потому что оно связано с вашей средой.

max_connections=1000

Добавление этой строки в файл my.cnf может решить вашу проблему. Перезапустите службу MySQL, как только вы закончите с этим изменением.

Подробнее о Сервер MySQL ушел

Если он не работает, попробуйте эту функцию автоматического повторного подключения.

Ответ 3

Как сказано, MySQL в сценариях PHP истекает время, когда нет связи между ними в течение некоторого времени. Это хорошо, поскольку незанятые соединения будут потреблять ресурсы сервера.

Ошибка "Сервер ушел", как правило, происходит, когда между двумя запросами происходит относительно длительное вычисление.

Чтобы предотвратить это, вы можете

  • Периодически выполняйте запрос SELECT 1 во время выполнения
  • Создайте обертку вокруг ваших запросов, которая проверяет правильность подключения до выполнения
  • Используйте ответ из этого сообщения

Тем не менее, я считаю, что перенастройка MySQL для более долгого открытия соединения поощряет небрежное программирование и будет советовать ему.

Ответ 4

Это может быть также размер запроса, так как иногда ORM объединяют запросы для повышения производительности.

Попробуйте установить max_allowed_packet = 128M, по крайней мере, должны быть полезны для диагностики.

Ответ 5

Если ваша БД не обрабатывает несколько одновременных подключений и запросов, вы можете установить "бесконечные" таймауты. Это существенно не повлияет на ресурсы базы данных. Наилучший подход - отправить пакеты ping (SELECT 1), чтобы продлить время ожидания и сделать соединение сохраненным.

Ответ 6

Чтобы решить эту проблему, я предлагаю вам:

  • Распределите свои процессы с помощью сервера заданий Gearman (http://gearman.org/)
  • Управление этими процессами легко с помощью Supervisor (http://supervisord.org/)

Вот как.

Запустите приложение веб-сокета как демон, как и вы уже сделали (возможно, используя cron). Или даже лучше, управляйте им с помощью Supervisor. Настройте его так, чтобы Супервизор запустил его при запуске Supervisor и автозапуска демона, если он умрет.

Пример конфигурации:

[program:my-daemon]
command=/usr/bin/php /path/to/your/daemon/script
autostart=true
autorestart=true

Затем вместо того, чтобы выполнять обработку запросов внутри демона приложения, создайте обработчик Gearman для его обработки. После регистрации Работник будет ожидать, чтобы его выполняли/вызывали. Вы должны позвонить Работнику из своего приложения websocket вместе с необходимым параметром рабочей нагрузки (см. Веб-сайт Gearman для объяснения этой рабочей нагрузки).

В Worker установите его для остановки/выхода, когда он уже завершит задание, запрошенное демоном. При этом у вас не будет проблемы с сервером mysql, потому что соединение немедленно закрывается.

Наконец, мы должны сделать Worker доступным все время, как демон. Таким образом, подобно демону, настройте Supervisor для автозапуска и автозапуска, когда Рабочий умирает/останавливается, например:

[program:my-worker]
command=/usr/bin/php /path/to/your/worker/script
autostart=true
autorestart=true

Еще одна интересная вещь: вы можете добавить столько Рабочих, сколько хотите жить. Просто добавьте следующую конфигурацию:

numprocs=9 #change it to any number
process_name=%(program_name)s_%(process_num)02d #to identity worker number

Поскольку мы сказали Супервизору автозапускать каждый процесс, у нас всегда есть постоянный рабочий, работающий в фоновом режиме.

Вот еще одно объяснение этой стратегии: http://www.masnun.com/2011/11/02/gearman-php-and-supervisor-processing-background-jobs-with-sanity.html

Надеюсь, что это поможет!