отладка длинного сценария PHP

У меня есть php-скрипт, работающий как cron-задание, широко использующий сторонний код. Сам скрипт имеет несколько тысяч LOC. В основном это сценарий импорта / обработки данных. (JSON для MySQL, но он также делает много HTTP-вызовов и некоторых SOAP).

Теперь производительность понижается со временем. При тестировании с несколькими записями (около 100) производительность работает нормально, это делается через 10-20 минут. При запуске всего импорта (около 1600 записей) среднее время импорта одной записи растет устойчиво, а целая вещь занимает более 24 часов, поэтому по крайней мере в 5 раз дольше, чем ожидалось.

Память, похоже, не проблема, использование растет, как должно, без неожиданных пиков.

Поэтому мне нужно отладить его, чтобы найти узкое место. Это может быть проблема с скриптом, базовой базой кода, самой php, базой данных, os или сетевой частью. На данный момент я подозреваю какое-то кэширование где-то, что плохо ведет себя с коэффициентом около 100% пропусков.

Я не могу использовать XDebug, файл профиля растет слишком быстро, чтобы поддаваться лечению.

Итак, вопрос: как я могу отлаживать этот сценарий?

Версия PHP: 5.4.41 ОС: Debian 7.8 При необходимости я могу иметь привилегии root и устанавливать инструменты. Но это производственный сервер, и в идеале отладка не должна быть слишком сложной.

Инструмент профилирования:

Существует инструмент профилирования PHP под названием Blackfire, который в настоящее время находится в открытой бета-версии. Существует конкретная документация о том, как профилировать приложения CLI . После того, как вы собрали профиль, вы можете проанализировать поток управления приложениями с помощью измерений времени в хорошем пользовательском интерфейсе: Инструмент Blackfire

Недостаточное потребление памяти:

Память, похоже, не проблема, использование растет, как должно, без неожиданных пиков.

Растущее использование памяти на самом деле звучит подозрительно! Если текущий набор данных не зависит от всех предыдущих наборов данных импорта, то растущая память, скорее всего, означает, что все импортированные наборы данных хранятся в памяти, что плохо. PHP также может часто пытаться собрать мусор, просто чтобы узнать, что извлечь из памяти нечего. Особенно тяжелые задачи CLI затронуты, поэтому обязательно прочитайте сообщение в блоге, которое обнаружило поведение.

Да, это возможно, и вы можете использовать Kint (PHP Debugging Script)

Что это? Kint for PHP – это инструмент, предназначенный для представления ваших отладочных данных в максимально возможной степени.

Другими словами, это var_dump () и debug_backtrace () на стероидах. Прост в использовании, но мощный и настраиваемый. Важное дополнение к вашему инструменту разработки.

Все еще потеряно? Вы используете его для просмотра внутренних переменных.

введите описание изображения здесь

Действуйте как заменитель debug_backtrace, тоже введите описание изображения здесь

вы можете скачать здесь или здесь

Общая документация и помощь здесь

Кроме того, он также поддерживает почти все php-рамки

  • CodeIgniter
  • Drupal
  • Symfony
  • Symfony 2
  • WordPress
  • Yii
  • фреймворк
  • Zend Framework

Всего наилучшего…. 🙂

На ум приходят три вещи:

  1. Настройте IDE, чтобы вы могли отлаживать скрипт PHP по строкам
  2. Добавить некоторые записи в скрипт
  3. Ищите длительные запросы в MySQL

Вариант отладки №2 является самым простым. Поскольку это выполняется как задание cron, вы добавляете в свой скрипт echo в сценарии:

 <?php function log_message($type, $message) { echo "[{strtoupper($type)}, {date('dmY H:i:s')}] $message"; } log_message('info', 'Import script started'); // ... the rest of your script log_message('info', 'Import script finished'); 

Затем передайте stdout в файл журнала в команде задания cron.

 01 04 * * * php /path/to/script.php >> /path/to/script.log 

Теперь вы можете добавить log_message('info|warn|debug|error', 'Message here') по всему скрипту и по крайней мере получить представление о том, где проблема с производительностью.

Вариант отладки № 3 – это просто работа по поиску в MySQL. Один из ваших запросов может занять много времени, и он может появиться в долгосрочной утилите запросов для MySQL.

Используйте strace чтобы увидеть, что программа в основном делает с точки зрения системы. Он висит в операциях ввода-вывода и т. Д.? strace – это первое, что вы пытаетесь решить, столкнувшись с проблемами производительности с любым видом приложения Linux. Никто не может скрыться от него! 😉

Если вы узнаете, что программа зависает в сетевых вызовах, таких как connect , readfrom и друзья, это означает, что сетевая связь действительно зависает в какой-то момент при подключении или ожидании ответов, чем вы можете использовать tcpdump для анализа этого.

Используя вышеприведенные методы, вы сможете найти наиболее распространенные проблемы с производительностью. Обратите внимание, что вы можете даже привязать к выполняемой задаче с помощью strace используя -p PID .


Если описанные выше методы не помогают, я бы профилировал скрипт с помощью xdebug . Вы можете анализировать выход профилировщика с помощью таких инструментов, как KCachegrind

Хотя это не оговорено, и если моя догадка правильная, вы, кажется, занимаетесь документами по одному, но в одном большом cron.

т. е. захватить запись # 1, как-нибудь ее отбросить, добавить к ней значение, переформатировать ее, затем сохранить, а затем перейти к записи №2

Я бы подумал о том, чтобы сломать большой cron. т.е.

Cron # 1: захватить все записи и кэшировать все важные данные локально (на этот сервер). Установите флаг, если эта стадия достигнута.

Cron # 2: теперь у вас есть необходимые данные, munge и добавьте значение, кэш, который выводит. Установите флаг, если эта стадия достигнута.

Cron # 3: переформатировать эти данные и сохранить их. Удалите все файлы.

Такое «разделение и завоевание» облегчит ваши проблемы с отладкой и приведет к лучшему пониманию того, что на самом деле происходит, и в качестве бонуса дает вам возможность повторить, cron 2.

Мне приходилось делать это много раз, и для меня регистрация – это ключ к выявлению слабых мест в вашем коде, выявлению плохих предположений о качестве данных и может намекать на то, что задержка вызывает проблему.

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

В моем случае проблема, которую я обнаружил, заключалась в том, что у меня были сетевые тайм-ауты по умолчанию, и многие веб-запросы просто теряли время.

В общем, хотя и не внешний инструмент, вы можете использовать разницу между двумя запросами microtime (TRUE) на временные разделы кода. Чтобы журнал был ограниченным, установите флаг и проверяйте только время, если флаг не уменьшился до нуля после сокращения для каждого такого события. Вы можете иметь отдельные флаги для отдельных сегментов кода или даже для разных временных интервалов в сегменте кода.

 $flag['name'] = 10; // How many times to fire $slow['name'] = 0.5; // How long in seconds before it's a problem? $start = microtime(TRUE); do_something($parameters); $used = microtime(TRUE) - $start; if ( $flag['name'] && used >= $slow['name'] ) { logit($parameters); $flag['name']--; } 

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

Конечно, это предполагает, что отдельные элементы вызывают вашу проблему, а не просто общее замедление с течением времени.

РЕДАКТИРОВАТЬ:

Я (сейчас) вижу, что это производственный сервер. Это делает редактирование кода менее приятным. Вероятно, вы захотите интегрировать с кодом минимальный процесс, имеющий логику тестирования и, возможно, поддерживаемые теги / флаги и количества во внешнем файле.

 setStart('flagname'); // Do stuff to be checked for speed here setStop('flagname',$moredata); 

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

xdebug_print_function_stack – это вариант, но то, что вы также можете сделать, это создать «трассировку функции». Существуют три выходных формата. Один из них предназначен для чтения человеком, а другой – для компьютерных программ, поскольку его проще разбирать, а последний использует HTML для форматирования трассировки

 http://www.xdebug.org/docs/execution_trace 

Хорошо, в основном у вас есть две возможности – это либо неэффективный PHP-код, либо неэффективный код MySQL. Судя по тому, что вы говорите, это, вероятно, вставляет в индексированную таблицу много записей отдельно, что приводит к увеличению времени вставки. Вы должны либо отключить индексы, либо перестроить их после вставки, либо оптимизировать код вставки.

Но об инструментах.

Вы можете настроить систему на автоматический журнал медленных запросов MySQL: https://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html

Вы также можете сделать то же самое с PHP-скриптами, но вам нужна среда PHP-FPM (и у вас, вероятно, есть Apache). https://rtcamp.com/tutorials/php/fpm-slow-log/

Эти инструменты очень мощные и универсальные.

PS 10-20 минут для 100 записей кажется ЛОТ.

Вы можете использовать https://github.com/jmartin82/phplapse для записи активности приложения для определения времени.

Например, начните запись после n итераций с помощью:

 phplapse_start(); 

И остановите его на следующей итерации с помощью:

 phplapse_stop(); 

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

(Я автор проекта, не стесняйтесь контактировать со мной, чтобы улучшить функциональность)

У меня есть аналогичная работа, выполняемая каждую ночь (задание cron для обновления моей базы данных). Я нашел наиболее надежный способ отладки – настроить таблицу журналов в базе данных и регулярно вставлять / обновлять строку json, содержащую многомерный массив, с информацией о каждой записи и любой полезной информацией, которую вы хотите знать о каждой записи. Таким образом, если ваша работа cron не закончится, у вас все еще есть подробная информация о том, где она встала и что произошло на этом пути. Затем вы можете написать простую страницу, чтобы вытащить строку json, вернуть ее обратно в массив и распечатать полезные данные на странице, включая время и пройденные тесты и т. Д. Когда вы видите что-то как проблему, вы можете сконцентрироваться на том, чтобы добавить больше информации из этой области в строку json.

Регулярная команда «top» может показать вам, если использование процессора php или mysql является узким местом. Если нет, то задержки могут быть вызваны http-вызовами.

Если использование процессора mysqld низкое, но постоянное, то это может быть узким местом использования диска.

Кроме того, вы можете проверить использование полосы пропускания, установив и используя «спидометр» или другие инструменты.