Наша команда работает над созданием плагинов WordPress и предоставляет размещенные экземпляры на нескольких независимых серверах. Наша установка WordPress управляется Git, на всех серверах установлена одна и та же установка источника и WordPress, только домены и фактические данные в базе данных различаются. Для каждой установки MySql работает на одном хосте. WordPress работает исключительно на каждом сервере.
Однако после развертывания этой установки на Windows Server 2008 RC2 мы заметили резкую разницу в производительности по сравнению с нашими другими серверами: время генерации страницы увеличивается с avg. От 400 до 4000-5000мс для страниц, сгенерированных с помощью PHP. Для статических ресурсов, поставляемых только Apache, скорость примерно такая же, как у linux.
Поэтому мы предприняли некоторые шаги, чтобы сузить проблему:
После некоторого профилирования мы быстро заметили, что оценка регулярных выражений на наших машинах Windows ужасно медленная. Оценка 10.000 Регулярных выражений ( preg_match
) занимает около 90 мс в Linux и 3000 мс в Windows.
Профилирование, системные тесты и детали конфигурации приведены ниже. Мы не хотим оптимизировать этот скрипт (который мы знаем, как это сделать). Мы хотим, чтобы сценарий работал примерно на той же скорости в окнах, что и на Linux (с той же настройкой относительно opcache / …). Не нужно также оптимизировать объем памяти сценария.
Обновление: через некоторое время системы, похоже, исчерпали память, вызывая исключения из памяти и случайные распределения. См. Ниже для более подробной информации. На данный момент исправление Apache / PHP исправило проблему.
Трассировка в _get_browser
:
File (called from) require wp-blog-header.php (index.php:17) wp (wp-blog-header.php:14) WP->main (functions.php:808) php::do_action_ref_array (class-wp.php:616) php::call_user_func_array (wp-includes/plugin:507) wp_slimstat::slimtrack (php::internal (507)) wp_slimstat::_get_browser (wp-slimstat.php:385)
Обновление 2 : Некоторая причина, по которой я не могу вспомнить, мы вернулись к активации PHP как модуля Apache на наших серверах (то же самое, что давало плохую производительность). Но сегодня они бегут невероятно быстро (~ 1сек / запрос). Добавление Opcache сводится к ~ 400ms / req. Apache / PHP / Windows остались прежними.
1) Результаты профилирования
Профилирование выполнялось с помощью XDebug на всех машинах. Обычно мы собрали всего несколько прогонов – этого было достаточно, чтобы выявить место, где большую часть времени (50% +) было потрачено: метод [get_browser][1]
плагина WordPress wp-slimstats
:
protected static function _get_browser(){ // Load cache @include_once(plugin_dir_path( __FILE__ ).'databases/browscap.php'); // browscap.php contains $slimstat_patterns and $slimstat_browsers $browser = array('browser' => 'Default Browser', 'version' => '1', 'platform' => 'unknown', 'css_version' => 1, 'type' => 1); if (empty($slimstat_patterns) || !is_array($slimstat_patterns)) return $browser; $user_agent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:''; $search = array(); foreach ($slimstat_patterns as $key => $pattern){ if (preg_match($pattern . 'i', $user_agent)){ $search = $value = $search + $slimstat_browsers[$key]; while (array_key_exists(3, $value) && $value[3]) { $value = $slimstat_browsers[$value[3]]; $search += $value; } break; } } // Lots of other lines to relevant to the profiling results }
Эта функция, аналогичная функции get_browser
PHP, обнаруживает возможности браузера и ОС. Большая часть времени выполнения скрипта проводится в этом цикле foreach
, оценивая все эти preg_match
(~ приблизительно 8000 – 10000 на запрос страницы). Это занимает около 90 мс в Linux и 3000 мс в Windows. Результаты были одинаковыми для всех тестируемых установок (на рисунке показаны данные двух исполнений):
Конечно, загрузка двух огромных массивов занимает некоторое время. Оценка регулярных выражений. Но мы ожидаем, что они возьмут примерно то же самое время на Linux и Windows. Это результат профилирования в linux vm (только для одной страницы). Разница довольно очевидна:
Другим убийцей времени был Object-Cache WordPress:
function get( $key, $group = 'default', $force = false, &$found = null ) { if ( empty( $group ) ) $group = 'default'; if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) $key = $this->blog_prefix . $key; if ( $this->_exists( $key, $group ) ) { $found = true; $this->cache_hits += 1; if ( is_object($this->cache[$group][$key]) ) return clone $this->cache[$group][$key]; else return $this->cache[$group][$key]; } $found = false; $this->cache_misses += 1; return false; }
Время расходуется внутри самой этой функции (3 исполнения скриптов):
В linux:
Последним настоящим убийцей было время перевести. Каждый перевод, загруженный из памяти, занимает от 0.2 мс до 4 мс в WordPress:
В linux:
2) Протестированные системы
Чтобы убедиться, что виртуализация или Apache действительно влияют на это, мы протестировали это на нескольких настройках. Антивирус был отключен во всех настройках:
Результаты профилирования, как упоминалось выше, были одинаковыми для разных систем (выход ~ 10%). Windows всегда была значительным фактором медленнее, чем Linux.
Использование новой установки WordPress & Slimstats привело к прибл. те же результаты. Переписывание кода здесь не является вариантом.
Обновление . Между тем мы обнаружили две другие системы Windows (как Windows 2008 R2, VM и Phys), где этот полный стек работает довольно быстро. Тем не менее, такая же конфигурация.
Обновление 2 : Запуск PHP в качестве модуля apache на Life-Servers был немного быстрее, чем метод fastcgi: до ~ 2 секунд, на 50% меньше.
Запуск памяти
Через некоторое время наш Live-сервер перестает работать, вызывая эти исключения из памяти:
PHP Fatal error: Out of memory (allocated 4456448) (tried to allocate 136 bytes) PHP Fatal error: Out of memory (allocated 8650752) (tried to allocate 45 bytes) PHP Fatal error: Out of memory (allocated 6815744) (tried to allocate 24 bytes)
Это происходит случайно. Очевидно, что менеджер памяти Zend не может выделить больше памяти, хотя скриптам будет разрешено это сделать. В то время, когда инцидент, на сервере было около 50% свободной памяти (2 ГБ +). Таким образом, на сервере фактически не хватает бара. Перезапуск Apache / PHP исправил эту проблему на данный момент.
Не уверен, что эта проблема связана с проблемами производительности здесь. Тем не менее, поскольку оба вопроса, по-видимому, связаны с памятью, он включен здесь. Особенно мы попытаемся воспроизвести настройки Windows-тестов, обеспечивающие достойную производительность.
3) Конфигурация Apache и PHP
… вероятно, не имеют общих ошибок. Буферизация вывода включена (по умолчанию), многобайтовое переопределение отключено, … Если какие-либо опции (ы) представляют интерес, мы с радостью их предоставим.
Вывод httpd.exe -V
Server version: Apache/2.4.7 (Win32) Apache Lounge VC10 Server built: Nov 26 2013 15:46:56 Server's Module Magic Number: 20120211:27 Server loaded: APR 1.5.0, APR-UTIL 1.5.3 Compiled using: APR 1.5.0, APR-UTIL 1.5.3 Architecture: 32-bit Server MPM: WinNT threaded: yes (fixed thread count) forked: no Server compiled with.... -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses disabled) -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/apache" -D SUEXEC_BIN="/apache/bin/suexec" -D DEFAULT_PIDLOG="logs/httpd.pid" -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error.log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"
Конфигурация mpm_winnt_module
:
<IfModule mpm_winnt_module> ThreadsPerChild 150 ThreadStackSize 8388608 MaxConnectionsPerChild 0 </IfModule>
Выдержка из php.ini:
realpath_cache_size = 12M pcre.recursion_limit = 100000
4) Текущая подозреваемая причина
Старое предположение:
Все три примера сильно зависят от больших массивов и операций с строками. Что-то вроде обычной фабрики. Поскольку реализация работает нормально в Linux, мы подозреваем, что это проблема с памятью в Windows. Учитывая отсутствие взаимодействия с базами данных в местах с пин-точками, мы не подозреваем, что проблема с базой данных или сервером <-> PHP. Как-то взаимодействие с памятью PHP просто кажется медленным. Может быть, кто-то мешает памяти на Windows сделать доступ значительно медленнее?
Старое предположение 2:
Поскольку один и тот же стек отлично работает на других машинах Windows, мы предполагаем, что проблема находится где-то в конфигурации Windows.
Новое предположение 3:
На самом деле, я не уверен в своих предположениях. Зачем запускать PHP намного медленнее, чем fastcgi, тогда как модуль apache>
Любые идеи о том, как проверить это или найти настоящую проблему здесь? Любая помощь или руководство для решения этой проблемы приветствуется.
В Windows есть множество служб / политик, которые ограничивают, предотвращают, защищают, контролируют и т. Д. Использование компьютера в любой ситуации.
Хороший специалист, сертифицированный Microsoft, сможет решить ваш вопрос в течение нескольких минут, потому что у них будет возможность точно определить, какие настройки / службы / политики проверять и отключать / включать / изменять настройки, чтобы скрипты PHP выполнялись быстрее.
Из моей памяти я могу только предложить вам проверить все, что касается ОЗУ, доступа к жесткому диску, переменных среды, ограничений и безопасности (например, брандмауэра). Все, что может повлиять на выполнение скрипта php, начиная с некоторых политик вызова удаленного управления и заканчивая памятью рабочего стека.
Логика заключается в том, что php.exe вызывает некоторый внешний .dll-файл для выполнения какой-либо операции, могут быть проверки на пути, выполняемые ОС, что замедлит отправку запроса через такую DLL и получит от него ответ. Если .dll использует жесткий диск для доступа к чему-либо – политики доступа к жесткому диску входят в сцену. Кроме того, как все находится в памяти – в ОЗУ или жестком диске ОЗУ. Политика применения. Темы. Ограничения на максимальный процент, доступный для использования в приложениях.
Я не говорю о том, что хосты на базе Windows плохие, просто для них сложнее настроить для обычного администратора. Если у вас есть специалист Microsoft на руках, он может настроить ваш сервер так же быстро, как сервер на базе Linux.
включить APC при использовании PHP5.4
если вы не заметили прирост скорости, когда APC включен, что-то неправильно настроено
[APC] extension=php_apc.dll apc.enabled=1 apc.shm_segments=1 apc.shm_size=128M apc.num_files_hint=7000 apc.user_entries_hint=4096 apc.ttl=7200 apc.user_ttl=7200
включить Zend Opcode, когда на PHP 5.5
[Zend] zend_extension=ext/php_zend.dll zend_optimizerplus.enable=1 zend_optimizerplus.use_cwd=1 zend_optimizerplus.validate_timestamp=0 zend_optimizerplus.revalidate_freq=2
zend_optimizerplus.revalidate_path=0 zend_optimizerplus.dups_fix=0 zend_optimizerplus.log_verbosity_level=1 zend_optimizerplus.memory_consumption=128 zend_optimizerplus.interned_strings_buffer=16 zend_optimizerplus.max_accelerated_files=2000 zend_optimizerplus.max_wasted_percentage=25 zend_optimizerplus.consistency_checks=0 zend_optimizerplus.force_restart_timeout=60 zend_optimizerplus.blacklist_filename= zend_optimizerplus.fast_shutdown=0 zend_optimizerplus.optimization_level=0xfffffbbf zend_optimizerplus.enable_slow_optimizations=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1
отключите WordPress расширения шаг за шагом, чтобы найти использование памяти монстра
define('WP_MEMORY_LIMIT', '128M');
, если вы не используете плагины для конвертирования изображений, которые должны быть достаточными ini_set('memory_limit', -1);
memory_get_usage
и распределите вызовы по всей системе, чтобы найти положение кода, где утечка памяти zend.enable_gc=1
попробовать, скрипты будут медленнее, но использовать меньше памяти Я взглянул на этот плагин на Github:
https://github.com/wp-plugins/wp-slimstat
И включенный файл-нарушитель – это файл, который в какой-то степени был минимизирован и действительно является данными (а не кодом), есть 5 вариантов, каждый из которых составляет около 400 КБ
Существует также файл maxmind.dat размером 400 КБ, хотя я не знаю, использует ли он оба.
Вы используете более старую версию плагина, версия 3.2.3, и есть намного более новая, которая может решить вашу проблему.
Сравнение различий сложно, потому что автор или тот, кто не сохранил историю git в порядке, поэтому мне пришлось вручную разбить файл. Большинство изменений, связанных с _get_browser, похоже, добавляют кэш.
Возможно, загрузка этого файла медленна для синтаксического анализа, но я ожидаю, что PHP будет загружать оба файла с одинаковой скоростью на обеих платформах, обеспечивающих работу кэширования IO.
EDIT Ищите немного ближе, что может не решить вашу проблему. Эти файлы представляют собой в основном большие таблицы поиска регулярных выражений. У вашей системы Linux был кеш APC, и этого нет? Кэш APC, вероятно, сохранит кеширование файлов PHP (хотя и не скомпилированные шаблоны регулярных выражений)
Используйте NGINX и FCGI для PHP через сокет UNIX (не TCP-сокет).
http://wiki.nginx.org/PHPFcgiExample
Вы сразу заметите улучшения скорости даже без ускорителей. Кроме того, выше настройки будут иметь гораздо меньший объем использования памяти.
Чтобы устранить проблему такого рода, вам необходимо:
Если это так, вам нужно удалить обновление Windows и / или программное обеспечение, вызывающее проблему, полностью завершить работу вашего сервера, а затем повторно установить обновление или программное обеспечение (чтобы обеспечить стабильное состояние во время установки).
Инструменты, которые могут помочь вам устранить эту проблему, включают в себя пакет sysinternals: http://technet.microsoft.com/en-us/sysinternals/bb842062.aspx
Или, проще говоря, сценарии VBS с открытым кодом для создания сопоставимых списков обновлений и приложений в системе.