Свеже-варимое ядро D7 и активно развивающийся корпоративный портал регулярно приводят к ситуациям, когда возникают "тормоза" с открытием тех или иных страниц портала. Композитный сайт до портала доберется еще не скоро, а решать вопросы производительности надо прямо сейчас.
Первое что пришло в голову - xdebug и модуль bitrix.xdebug от Дениса Шаромова. Отличный вариант, но под реальной нагрузкой в "стесненных" условиях сервера попытка снять трейс проблемного кода приводит к OutOfMemory и иным неприятным сюрпризам. Особенно когда требуется отловить 1 из 30-50 запросов к странице.
Следующим шагом стал совет Александра Сербула - использовать XHProf, который ощутимо легче в ресурсах и позволяет использовать его в боевых условиях под нагрузкой.
Итогом стал небольшой скрипт, позволяющий облегчить применение XHProf в проектах на битрикс.
Настройка:
$xhprof_path = '/home/bitrix/www/xhprof/'; // Путь к GUI XHProf, слэш на конце обязателен $xhprof_url = 'http://vhost/xhprof/xhprof_html/index.php'; // Базовая ссылка на GUI XHPro $xhprof_namespace = 'cp'; // Расширение файлов профиля (используется для агрегации и сравнительного анализа)
Ньанс - не всегда получается найти GUI в дебрях /usr/, поэтому я просто отдельно скачал его здесь pecl.php.net/get/xhprof-0.9.2.tgz и разместил в папке проекта.
time_limit: максимальное время выполнения страницы, для которой выполняется профилирование если 0, профилируются все запросы.
sendUser: если указан пользователь, то ссылка отправляется сообщением социальной сети интранет (требуется модуль Веб-мессенджер) если 0, формируется popup-окно для текущего пользователя.
Информация по установке XHProf: - VMBitrix: модуль доступен сразу, достаточно переименовать /etc/php.d/xhprof.ini.disabled в xhprof.ini - CentOS: yum install php-xhprof - другие системы - гугль в помощь...
// Путь к GUI XHProf, слэш на конце обязателен
$xhprof_path = '/home/bitrix/www/xhprof/';
// Базовая ссылка на GUI XHProf
$xhprof_url = 'http://vhost/xhprof/xhprof_html/index.php';
// Расширение файлов профиля (используется для агрегации и сравнительного анализа
$xhprof_namespace = 'cp';
$xhprof_start_time = 0;
$xhprof_time_limit = 0;
$xhprof_user = 0;
if (extension_loaded('xhprof')) {
include_once $xhprof_path.'xhprof_lib/utils/xhprof_lib.php';
include_once $xhprof_path.'xhprof_lib/utils/xhprof_runs.php';
}
/*
* time_limit: максимальное время выполнения страницы, для которой выполняется профилирование
* sendUser: если указан пользователь, то ссылка отправляется сообщением социальной сети интранет
*/
function xhprof_start($time_limit = 0, $sendUser = 0) {
global $xhprof_start_time;
global $xhprof_time_limit;
global $xhprof_user;
$xhprof_user = $sendUser;
$xhprof_start_time = time();
$xhprof_time_limit = $time_limit;
if (extension_loaded('xhprof'))
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
}
function xhprof_stop() {
global $xhprof_namespace;
global $xhprof_url;
global $xhprof_start_time;
global $xhprof_time_limit;
global $xhprof_user;
global $APPLICATION;
$run_time = (time() - $xhprof_start_time);
if ($xhprof_time_limit == 0 || ($run_time >= $xhprof_time_limit)) {
if (extension_loaded('xhprof')) {
$xhprof_data = xhprof_disable();
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, $xhprof_namespace);
$profiler_url = sprintf($xhprof_url.'?run=%s&source=%s', $run_id, $xhprof_namespace);
if ($xhprof_user>0) {
$arMessageFields = array(
"TO_USER_ID" => $xhprof_user,
"FROM_USER_ID" => 0,
"NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
"NOTIFY_MODULE" => "im",
"NOTIFY_MESSAGE" =>
"XHProf: (".$run_time.">=".$xhprof_time_limit."): <a target=_blank href=\"".$profiler_url."\">".$run_id.".".$xhprof_namespace."</a>\n".
"URL: <a target=\"_blank\" href=\"".$APPLICATION->GetCurUri()."\">".$APPLICATION->GetCurUri()."</a>"
);
CIMNotify::Add($arMessageFields);
}
else {
?><div style="
margin: 5px;
width: 300px;
font-size: 11px;
padding: 5px;
line-height: normal;
font-weight: normal;
position: fixed;
top: 10px;
left: 10px;
z-index: 999;
opacity: 1;
border: 1px solid blue;
font-family: Verdana,Arial;
font-size: 10px;
color: black;
background-color: white;
margin: 0px;
padding: 2px;
font-weight: normal;
line-height: normal;
text-align: left;
background-image: none;">
<b>XHProf</b> (<?=$run_time?>>=<?=$xhprof_time_limit?>): <a target=_blank href="<?=$profiler_url?>"><?=$run_id?>.<?=$xhprof_namespace?></a>
</div><?
}
}
}
}
?>
Примеры уведомлений и пример отчета XHProf в картинках:
В документации для продакшен рекомендуется флаг xhprof_enable(XHPROF_FLAGS_MEMORY)
CPU timer (getrusage) on Linux has high overheads. It is also coarse grained (millisec accuracy rather than microsec level) to be useful at function level. Therefore, the skew in reported numbers when using XHPROF_FLAGS_CPU mode tends to be higher.
We recommend using elapsed time + memory profiling mode in production. [Note: The additional overhead of memory profiling mode is really low.]
// elapsed time profiling (default) + memory profiling xhprof_enable(XHPROF_FLAGS_MEMORY);
Использовал на практике, спасибо! Столкнулся только с одной проблемой. Долго не мог понять, почему не сохранялись отчеты в одном фрагменте кода. Как оказалось, в одном из задействованных методов выполнение обработки заканчивалось функцией die(); соответственно дело до выполнения xhprof_stop() просто не доходило.
Плотников Павел, вы можете использовать register_shutdown_function(). Если нужно отслеживать выполнение целых страниц, а не кусков кода, то удобно использовать что-то типа такого:
Этот код убирается в отдельный файл, который затем вставляется в начало страниц. Либо, можно использовать параметр php auto_prepend_file и указать у него путь до этого файла. В этом случае, код будет подключаться сам, без необходимости правки всех скриптов.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».