В своей предыдущей статье по оптимизации сайта на WordPress я рассказал об очень эффективном подходе к оптимизации за счёт кэширования страниц. В результате чего для незалогиненных пользователей время ожидания страницы клиентом (исключая время на установление TLS-сессии) сократилось с 820 мс до 30 мс (этот и все последующие замеры проводились с сервера, расположенного в том же городе, что и мой VDS), что, согласитесь, является отличным показателем. Однако, для залогиненных пользователей генерация страницы происходила по-прежнему долго — в среднем 770 мс на сервере. В этой части я расскажу о том, как я сократил это время до 65 мс, при этом полностью сохранив работоспособность пользовательского функционала.

Целью этой и предыдущей статей является моё желание показать возможность оптимизации сайтов не только на WordPress, а вообще любого веб-приложения. Поэтому я использую такое количество инструментов, и так детально разбираю их конфигурацию. Если же Вам просто нужно ускорить WordPress — установите плагин WP Super Cache. Если Вас, как и меня, интересуют технологии, позволяющие оптимизировать любой сайт, а также Вам интересно, что стоит учитывать при разработке веб-приложений, рассчитанных на высокие нагрузки — прошу под кат, но только после прочтения первой части — дорабатывать я буду ту же систему.

Отключаем лишнее


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

Итак, среднее время генерации динамической страницы для залогиненного пользователя — 770 мс. Время генерации измерялось путём добавления в исходник index.php строчек:

<?php
    $t = microtime(1);
    //содержимое index.php
    echo '<!--Сгенерировано за: ' . (microtime(1) - $t) . 's' . '-->';
?>

Далее я провёл предварительный стресс-тест: 50 пользователей одновременно обращаются к динамически генерируемой странице, как только загрузка страницы завершена — тут же идёт следующий запрос.



Максимальное время ответа в данном случае составило 45.721 секунды.

Следуем распространённому совету, в кругах оптимизаторов WordPress, а именно — отключаем ненужные плагины. В моём блоге практически в каждой статье приводятся листинги кода/конфигурации. Для этих целей я использовал плагин Crayon Syntax Highlighter, рекомендуемый чуть ли не каждым блогером как лучшее средство для подсветки синтаксиса в листингах. Однако, сама подсветка мне была не столь уж и нужна, главное — аккуратно оформленное окошечко со скролл-баром с поддержкой табуляции. В целях тестирования попробовал отключить этот плагин. Результат — вместо 770 мс генерация страницы происходила в среднем за 170 мс, что в 4.5 раза быстрее. Однако, листинги теперь не были оформлены — текст статей сливался с кодом. Далее в поисках замены я наткнулся на плагин wp-syntax, который имел не такой богатый функционал как Crayon Syntax Highlighter, но со своей главной задачей — подсветкой синтаксиса, справлялся на ура. Среднее время генерации страницы с этим плагином составило в среднем 235 мс, что, в общем-то, приемлемо. Здесь я повторно провёл стресс-тест. Результаты следующие:



Максимальное время ответа теперь составило 9.897 секунды. Разница колоссальная.

Однако, как я уже говорил выше, подсветка для меня была не так уж и важна. Поэтому я решил удалить и этот плагин тоже, а вместо этого в стилях темы для тега <pre> (в него заключались листинги кода в вышеописанных плагинах) прописать следующие стили:
pre {
    max-height: 700px;
    margin-bottom: 20px;
    overflow: auto;
    background: #f0f0f0;
    padding: 10px;
    -webkit-box-shadow: 0px 0px 3px 0px rgba(0,0,0,0.75);
    -moz-box-shadow: 0px 0px 3px 0px rgba(0,0,0,0.75);
    box-shadow: 0px 0px 3px 0px rgba(0,0,0,0.75);
}

Листинги приняли следующий вид:



Этого мне было достаточно. Время генерации страницы на сервере — 170 мс. Проводим стресс-тест:



Максимальное время ответа теперь 7.002 секунды. Разница ощутима, оставляю этот вариант.

Далее я убрал из футера 2 виджета — последние комментарии и облако тегов. За счёт уменьшения количества запросов к БД среднее время генерации страницы на сервере сократилось до 155 мс. Результаты стресс-теста потерял, но максимальное время ответа составляло примерно 6.5 секунды.

Выбор движка таблиц в MariaDB


В комментариях к предыдущей статье мне указали на то, что использование MyISAM в качестве движка таблиц в СУБД MariaDB необоснованно. Согласен, исправляюсь. Выбирать буду между MyISAM, Aria, XtraDB.

Конфигурация my.cnf в случае с MyISAM и Aria будет одинаковой, и не отличается от конфигурации, приведённой в первой статье. Однако, для Aria следует использовать директиву default-storage-engine=Aria.

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

key_buffer = 32M #Уменьшаем буфер для ключей таблиц MyISAM и Aria
#skip-innodb #Включаем обратно движок XtraDB 
#default-storage-engine=MyISAM #По-умолчанию теперь будет XtraDB
innodb_buffer_pool_size = 128M #Размер буфера для кэширования данных и индексов XtraDB
innodb_flush_method = "O_DIRECT" #Выбираем более надёжный метод сброса данных из памяти на диск

В качестве критерия выбора я сначала хотел использовать время генерации страниц, однако все типы движков при простом запросе страницы показывали одни и те же результаты, поэтому было решено произвести стресс-тесты с нагрузкой в 100 пользователей. Мой тестовый аккаунт не позволял запускать стресс-тесты с количеством пользователей более 50, однако, как было выяснено опытным путём, было возможно запускать несколько тестов из разных вкладок браузера одновременно, чем я и воспользовался. Проверяем MyISAM. Пользователей: 100 (в другой вкладке чуть раньше запущен такой же тест):



Максимальное время ответа: 17.473 секунды.

Проверяем Aria. Пользователей: 100 (в другой вкладке чуть раньше запущен такой же тест):



Максимальное время ответа: 14.223 секунды. При этом более плавное изменение времени ответа.

Проверяем XtraDB. Пользователей: 100 (в другой вкладке чуть раньше запущен такой же тест):



Максимальное время ответа: 14.355 секунды.

Так как результаты на XtraDB и на Aria оказались почти одинаковыми, оставив при этом позади MyISAM, я решил пойти дальше и увеличивать число запросов, пока сайт на ляжет. На каком движке выдержит большую нагрузку — тот и выберу.

Снова тестируем Aria. Пользователей: 150 (в других двух вкладках чуть раньше запущены такие же тесты):



Упало на 148 пользователях. Время ответа: 19.311 секунды.

Тестируем XtraDB. Пользователей: 150 (в других двух вкладках чуть раньше запущены такие же тесты):



Упало на 150 пользователях. Время ответа: 20.963 секунды. В целом, результаты на Aria и XtraDB почти одинаковые. Но я решил выбрать XtraDB, потому что в дальнейшем хочу реализовать запрос данных для страниц со статьями с помощью NoSQL-решения HandlerSocket, работающее только с InnoDB/XtraDB. В данный момент существует расширение, компилируемое для PHP (php-handlersocket) и библиотека, написанная на PHP (HSPHP). Расширение я успел попробовать совместно с PHP5.6. С его помощью я сделал выборку из БД по PRIMARY KEY примерно в 2.5 раза быстрее, чем с помощью SQL-запроса. Но под PHP7 оно компилироваться отказывается (а следующим шагом у нас будет именно переход на новую версию). Что касается библиотеки HSPHP — замерив время получения выборки с её помощью и с помощью SQL-запроса (с учётом времени на подключение библиотеки), я выяснил, что при одиночном запросе в БД прироста она не даёт. Поэтому от её использования я отказался и теперь жду версии расширения, совместимой с PHP7.

Установка и настройка PHP7 + php-fpm


3 марта состоялся релиз новой версии PHP7.0.4. Её и будем ставить. Для начала удалим предыдущую версию PHP и php-fpm:

apt-get purge php5 php5-fpm

Скачиваем и распаковываем архив с исходниками:

mkdir /usr/local/src/php7-build
cd /usr/local/src/php7-build
curl -O http://se1.php.net/get/php-7.0.4.tar.bz2/from/this/mirror
tar jxf php-7.0.4.tar.bz2
cd php-7.0.4/

Устанавливаем всё необходимое для компиляции:

apt-get install build-essential libfcgi-dev libfcgi0ldbl libjpeg62-turbo-dbg libmcrypt-dev libssl-dev libc-client2007e libc-client2007e-dev libxml2-dev libbz2-dev libcurl4-openssl-dev libjpeg-dev libpng12-dev libfreetype6-dev libkrb5-dev libpq-dev libxml2-dev libxslt1-dev

Создаём симлинк, иначе не сможем скомпилить php с поддержкой imap.
ln -s /usr/lib/libc-client.a /usr/lib/x86_64-linux-gnu/libc-client.a

Создаём папку для установки:

mkdir /opt/php-7.0.4

Конфигурируем:

./configure --prefix=/opt/php-7.0.4 --with-pdo-pgsql --with-zlib-dir --with-freetype-dir --enable-mbstring --with-libxml-dir=/usr --enable-soap --enable-calendar --with-curl --with-mcrypt --with-zlib --with-gd --with-pgsql --disable-rpath --enable-inline-optimization --with-bz2 --with-zlib --enable-sockets --enable-sysvsem --enable-sysvshm --enable-pcntl --enable-mbregex --enable-exif --enable-bcmath --with-mhash --enable-zip --with-pcre-regex --with-pdo-mysql --with-mysqli --with-mysql-sock=/var/run/mysqld/mysqld.sock --with-jpeg-dir=/usr --with-png-dir=/usr --enable-gd-native-ttf --with-openssl --with-fpm-user=www-data --with-fpm-group=www-data --with-libdir=/lib/x86_64-linux-gnu --enable-ftp --with-imap --with-imap-ssl --with-kerberos --with-gettext --with-xmlrpc --with-xsl --enable-opcache --enable-fpm

Компилируем и устанавливаем (если у Вас VDS с теми же характеристиками, как у меня, можете смело идти пить кофе):

make
make install

Копируем конфиги:

cp /usr/local/src/php7-build/php-7.0.4/php.ini-production /opt/php-7.0.4/lib/php.ini
cp /opt/php-7.0.4/etc/php-fpm.conf.default /opt/php-7.0.4/etc/php-fpm.conf
cp /opt/php-7.0.4/etc/php-fpm.d/www.conf.default /opt/php-7.0.4/etc/php-fpm.d/www.conf

В файле /opt/php-7.0.4/etc/php-fpm.conf раскомментируем следующие строки:

pid = run/php-fpm.pid
events.mechanism = epoll

В файле /opt/php-7.0.4/etc/php-fpm.d/www.conf задаются те же настройки для воркеров, что и в первой статье. Директиву listen = 127.0.0.1:9000 Заменяем следующими строками:

listen = /var/run/php7.0.4-fpm.sock
listen.owner = www-data
listen.group = www-data


Соответственно, в настройках nginx'а в файле /etc/nginx/conf.d/backend.conf стоит указать директиву:

fastcgi_pass unix:/var/run/php7.0.4-fpm.sock;


При этом необходимо увеличить максимальное количество разрешенных подключений к Unix-сокету в системе, добавив в конец файла /etc/sysctl.conf директиву:

net.core.somaxconn = 65535


После этого необходимо перечитать конфиг:

sysctl -p /etc/sysctl.conf


В файле /opt/php-7.0.4/lib/php.ini задаём те же настройки, как и в первой статье. Для включения OPcache помимо этого добавляем строку:

zend_extension=opcache.so

Создаём симлинки для PHP и утилит:

ln -s /opt/php-7.0.4/bin/pear /bin/pear
ln -s /opt/php-7.0.4/bin/peardev /bin/peardev
ln -s /opt/php-7.0.4/bin/pecl /bin/pecl
ln -s /opt/php-7.0.4/bin/phar /bin/phar
ln -s /opt/php-7.0.4/bin/phar.phar /bin/phar.phar
ln -s /opt/php-7.0.4/bin/php /bin/php
ln -s /opt/php-7.0.4/bin/php-cgi /bin/php-cgi
ln -s /opt/php-7.0.4/bin/php-config /bin/php-config
ln -s /opt/php-7.0.4/bin/phpdbg /bin/phpdbg
ln -s /opt/php-7.0.4/bin/phpize /bin/phpize


Теперь создаем файл юнита systemd /etc/systemd/system/php-7.0.4-fpm.service:

[Unit]
Description=The PHP 7 FastCGI Process Manager
After=network.target

[Service]
Type=simple
PIDFile=/opt/php-7.0.4/var/run/php-fpm.pid
ExecStart=/opt/php-7.0.4/sbin/php-fpm --nodaemonize --fpm-config /opt/php-7.0.4/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

Активируем сервис и перезапускаем systemd:

systemctl enable php-7.0.4-fpm.service
systemctl daemon-reload

Осталось только запустить php-fpm:

systemctl start php-7.0.4-fpm.service

Проверяем скорость генерации страницы на сервере: 45 мс. По сравнению с предыдущим шагом производительность выросла в 3.4 раза. Проводим нагрузочное тестирование. 150 пользователей оказалось недостаточно, чтобы сайт упал. Открываем ещё одну вкладку с тестом. На 179 пользователях проскакивает ошибка (не полный крах), останавливаю тест. Максимальное время ответа: 8.966 секунды. В общем, переходите на PHP7.



Настройка Edge Side Includes (ESI) в Varnish


ESI — это язык для включения фрагментов веб-страниц в другие страницы. Это позволяет использовать кэшированные страницы, с использованием в них динамических элементов, сократив за счёт этого время генерации. Это аналог SSI.



В WordPress я решил это использовать для реализации следующей схемы: если пользователь залогинен, то генерировать на бэкенде для него только header, содержащий верхнее пользовательское меню. Для страниц с записями также динамически генерировать блок с комментариями. Всё остальное берётся из кэша. Также для сохранения высокой скорости отдачи для незалогиненных пользователей для каждой странички генерируется статический header и в последующем используется из кэша.

Так как теперь всё, что ниже header'а кэшируется, определить скорость генерации страницы с помощью PHP не получится. Для этого я использовал сервис Pingdom Tools, позволяющий оценить скорость загрузки сайта в целом, так и каждого документа. Выбрал я его по причине того, что у них возможно запустить тестирование с сервера, расположенного в Амстердаме, где и расположен мой VDS. За счёт этого время на установление соединения было в районе 20 мс и время генерации страницы после отправки запросу серверу при нескольких подряд отправленных запросах практически не менялось (± 5 мс). Перед активацией ESI я замерил скорость загрузки сайта с помощью этого сервиса. Вот результаты:



Сайт полностью загрузился за 360 мс, а время генерации самой страницы составило 135 мс. Приступим к настройке.

Немного изменим файл конфигурации VCL /etc/varnish/default.vcl, приведенный в первой части.

После блока в секции vcl_backend_response, описывающего работу со статическими файлами

# Для статических файлов, которые отдаёт бэкенд...
{
...
}

Добавляем следующий блок:

# Включаем обработку ESI для всех запросов, в URL которых отсутствует слово dynamic, можно сделать что-то более уникальное, так как такой запрос вполне уже может существовать
if (!(bereq.url ~ "dynamic")) {
        set beresp.do_esi = true;
}

Включение обработки будет включено только для тех случаев, для которых в секции vcl_recv не указан запрет обработки с помощью директивы set req.esi = false;

Блок кода в секции vcl_recv, отправляющий на бэкенд залогиненного пользователя

if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") {
        return (pass);
}

заменяем на блок:

# Для незалогиненных пользователей отключаем ESI. 
if (!(req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_")) {
        set req.esi = false;
}

А в самое начало секции vcl_recv помещаем следующий блок:

# Пропускать запросы, содержащие слово dynamic
if (req.url ~ "dynamic") {
        return (pass);
}

Теперь нужно заставить WordPress работать нужным нам образом. Но для начала расскажу о тегах, используемых в ESI, позволяющих сформировать 2 разные страницы в браузере, в зависимости от того, включена обработка ESI или нет.
  1.  Тэг комментария <!--esi--> — при условии включенной обработки ESI эти комментарии убираются, а то, что было ими закомментировано, при этом выполняется. Если же ESI отключено, то в браузере клиента данный участок кода будет интерпретирован как простой HTML-комментарий и будет проигнорирован;
  2. <esi:include src=«адрес/страницы»/> — если включено ESI, будет произведен инклуд этой страницы в данном месте. Если ESI выключено, будет передано в браузер в исходном виде. Поэтому заключается в вышеописанный тэг комментария;
  3. <esi:remove> ... </esi:remove> - если включено ESI, будет произведено удаление данного участка кода. Если ESI выключено, будет передано в браузер в исходном виде. Поэтому так же заключается в вышеописанный тэг комментария.

Итак, настраиваем WordPress. Код верхней пользовательской панели генерируется, как правило, в файле header.php. Однако в моём случае часть стилей была перемещена в footer.php в целях ускорения загрузки страницы и генерировалась при вызове функции wp_footer(). Первым делом я перенёс вызов этой функции в файл header.php, поместив после открывающего тэга <body> код:

<?php wp_footer(); ?>

и убрал его в файле footer.php.

Теперь отредактируем файл темы index.php. Вместо кода

<?php get_header(); ?>

напишем:

<?php 
$beforeHeader = '?';
if(isset($_GET['dynamic-header']) || isset($_GET['static-header'])) 
{
    get_header(); 
    die();
}
elseif ($_SERVER['QUERY_STRING'] != '')
{
    $beforeHeader = '&';
}
?>
<!--esi <esi:include src="<?php echo $_SERVER['REQUEST_URI'] . $beforeHeader; ?>dynamic-header"/> 
<esi:remove> -->
    <?php echo file_get_contents('http://webshake.ru'.$_SERVER['REQUEST_URI'] . $beforeHeader .'static-header'); ?>
<!--esi </esi:remove> -->

Если не поняли, поясню, как это работает. В переменной $beforeHeader лежит символ, который будет поставлен перед параметром в URL. Если строка запроса пуста, добавляем после адреса вопросительный знак и параметр, если нет — символ '&' и параметр. Если методом GET была установлена переменная dynamic-header или static-header, то выдать заголовок и умереть. Иначе, в случае включенного ESI (а для нас это означает, что перед нами залогиненный пользователь), запросить текущую страницу с добавлением ?dynamic-header. Так как в URL содержится слово dynamic, Varnish направит запрос на бэкенд. Затем будет загружена текущая страница с ?static-header на конце при помощи функции file_get_contents(), при этом никакие cookies от пользователя не дойдут до этой страницы и будет сформирован статический заголовок, предназначенный для гостей, который ввиду отсутствия в URL слова dynamic отправится в кэш.
В случае, если ESI отключено (зашёл гость), инклуда динамического заголовка не произойдёт, и будет отправлен закомментированный код. Теги <esi:remove> и </esi:remove> так же будут отправлены закомментированными, а код статического заголовка будет подгружен из кэша, либо сгенерируется и сохранится в кэш, если обращение к странице произошло впервые.

Производим аналогичную замену кода <?php get_header(); ?> во всех страницах темы, где он имеется.

Осталось только реализовать динамическую работу с комментариями на страницах записей. Для этого открываем шаблон single.php и ищем функцию, отвечающую за выдачу кода комментариев. В моём случае это функция comments_template(); и в начало кода помимо уже добавленного нами пишем:

<?php
if(isset($_GET['dynamic-comments'])) 
{
    comments_template( '', true );
    die();
}

А вместо вызова функции

<?php comments_template( '', true ); ?>

Пишем:
<!--esi <esi:include src="<?php echo $_SERVER['REQUEST_URI'] . $beforeHeader; ?>dynamic-comments"/> 
<esi:remove> -->
 <?php echo file_get_contents('https://webshake.ru'.$_SERVER['REQUEST_URI'] . $beforeHeader .'dynamic-comments'); ?>
<!--esi </esi:remove> -->


Работает всё точно так же как и в случае выше, только комментарии всегда будут выдаваться динамически, потому что если кто-то добавит комментарий, PURGE-запрос будет отправлен только на страницу, но не на page?static-comments и комменты для незалогиненных не обновятся, а так всё работает.

Перезапускаем varnish:

service varnish restart

Замеряем скорость загрузки сайта с включённым ESI:



Общее время загрузки сайта — 289 мс. Время генерации страницы составило при этом 65 мс. Таким образом, с помощью ESI мы ускорили генерацию страницы для пользователя чуть больше чем в 2 раза.

После этого я провёл финальное нагрузочное тестирование. Пользователей: 300 (в других пяти вкладках чуть раньше запущены такие же тесты):



Сервер упал при нагрузке в 276 залогиненных пользователей. Время ответа при этом составило 7.155 секунды.

Эффективное сжатие


В комментариях к предыдущей статье мне указали на то, что сжатие на выходе nginx со степенью сжатия 9 будет давать лишнюю нагрузку на процессор, при этом файлы сожмутся сильнее примерно на 5 процентов, по сравнению со степенью сжатия 1, дающей гораздо меньшую нагрузку на процессор.

Собственно, пришла мысль для сжатия статических файлов (.css, .js) использовать 9 степень и сжимать их на стороне бэкенда, после чего они будут попадать в кэш Varnish'а и уже без последующей нагрузки на процессор за счёт операции сжатия передаваться пользователю, а саму генерируемую страничку сжимать на фронтенде со степенью сжатия 1.

Таким образом и место в кэше сэкономим, и отдавать пользователям будем более мелкие файлы.

Для этого нужно в конфиге фронтенда nginx /etc/nginx/conf.d/frontend.conf прописать следующие директивы:

gzip on;
gzip_comp_level 1;
gzip_min_length 512;
gzip_buffers 8 64k;
gzip_types text/plain;
gzip_proxied any;

А в конфиге бэкенда /etc/nginx/conf.d/backend.conf:

gzip on;
gzip_comp_level 9;
gzip_min_length 512;
gzip_buffers 8 64k;
gzip_types text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
gzip_proxied any;

Никаких ощутимых результатов в скорости загрузки мне не дало, но размеры статических файлов всё же поуменьшились на несколько Кб. На супер-нагруженной системе это даст свои плоды.

Заключение


Помимо всего перечисленного можно дождаться выхода memcache для PHP7 и кэшировать запросы к БД с помощью плагина W3 Total Cache или какого-либо подобного. Также хотелось бы увидеть расширение php-handlersocket для PHP7, попробовать использовать для получения материалов по ID. Скорость доступа к этим данным должна возрасти раза в 2 точно. Большую часть советов, которые были даны в комментариях к прошлой статье выполнил. Спасибо большое комментаторам, благодаря Вам удалось разогнать сайт так, как я и не мечтал. Благодарю за прочтение.

Комментарии (44)


  1. antoo
    09.03.2016 20:53
    +3

    А чтобы не грузить сервер плагинами и не использовать голый <pre> без подсветки, можно найти JS библиотеку, highlight.js, например.


    1. gibson_dev
      09.03.2016 22:21
      +2

      А вдруг плагина то для WP и нету? И все, значит никак))))


      1. Sannis
        11.03.2016 03:21

        А что, WP настолько убог что не подволяет добавить пару строк HTML в шаблон?


        1. gibson_dev
          11.03.2016 07:38

          Ктож его спросит то при желании, просто большинство даже не буде насчет этого задумываться. А те кто задумываются WP не используют)


          1. jehy
            11.03.2016 10:29

            Неправда ваша. Есть много хороших проектов на вордпрессе, и много качественных плагинов. Принижать этот движок — всё равно, что говорить, что на PHP пишут только школьники.
            Кстати, вот и плагин с highlight.js.


  1. KriMs
    09.03.2016 22:16
    +2

    > gzip_comp_level 9;
    Лучше не ставить больше 6. Процессор на 9-ке грузится сильнее, а эффект сжатия после 6-ки минимальный.


    1. ivashkevitch
      09.03.2016 22:35
      -1

      Раз в сутки можно и "нагрузить". Файлов-то не более 10, не думаю, что это в данном примере вообще нагрузить процессор.


      1. NorthDakota
        10.03.2016 01:05
        -1

        Ну так дело в том что gzip юзается перед отдачей контента конечному пользователю
        Как результат, каждый запрос будет нагружать процесор


        1. InSys
          10.03.2016 01:13
          +1

          Ну насколько я понял, мысль у человека следующая — сжимать с 9 level и кешировать этот ответ в Varnish:

          Собственно, пришла мысль для сжатия статических файлов (.css, .js) использовать 9 степень и сжимать их на стороне бэкенда, после чего они будут попадать в кэш Varnish'а и уже без последующей нагрузки на процессор за счёт операции сжатия передаваться пользователю, а саму генерируемую страничку сжимать на фронтенде со степенью сжатия 1.
          в принципе в этом есть доля логики.


          1. NorthDakota
            10.03.2016 01:26
            -1

            Хм, интересно,
            хотя мне кажется отдавать статику через nginx будет быстрее
            минифицировать и отдавать, вот и весь секрет.

            Я так понимаю варниш слушает 80й порт?

            в таком случае ставим nginx на 80й, варниш например на 81
            и через nginx проксируем не статический трафик на варниш,
            варниш если нету готового кеша лезет в бекенд на 82й порт, на котором nginx уже дёргает пых и возвращается всё по цепочке вверх

            Как ни странно видел такую (бредовую) связку на одном хайлоад проекте, правда там вместо nginx был апач


            1. ivashkevitch
              10.03.2016 04:33
              -1

              Нет, схема следующая: nginx(установка SSL-соедиения + сжатие HTML со сжатием 1) — varnish — nginx (отдача статики со сжатием 9, .php передается на обработку fpm). Вот первая часть — https://habrahabr.ru/post/278189/

              Как мне кажется, получение фронтендом уже сжатых файлов из кэша Varnish'а будет происходит быстрее, чем сжатие и отдача nginx'ом каждый раз. Ну или добавить при этом кэширование сжатого nginx'ом в самом nginx'е (фронтенде). Вроде как говорят, будет ещё шустрее.


              1. igordata
                15.03.2016 15:15

                Не хочу портить наслаждение строительством мега-связок, но есть же gzip_static http://nginx.org/ru/docs/http/ngx_http_gzip_static_module.html

                просто рядом с каждым файлом должен лежать на харде файл с тем же именем и добавленным расширением .gz

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


  1. sepich
    10.03.2016 00:24
    +1

    Создаём init-скрипт /etc/init.d/php-7.0.4-fpm:
    chmod 755 /etc/init.d/php-7.0.4-fpm
    insserv php-7.0.4-fpm

    Теперь создаем файл юнита systemd /lib/systemd/system/php-7.0.4-fpm.service:
    systemctl enable php-7.0.4-fpm.service
    systemctl daemon-reload

    Вы уж определитесь, вы хотите sysv инит-скрипт или systemd юнит ;)
    В том виде, в котором вы написали, запускаться будет php-7.0.4-fpm.service и в /etc/init.d/php-7.0.4-fpm нет смысла.
    А так же неоднократно говорилось что не надо трогать грязными руками файлы в /lib/systemd/ — это место хранения вендор конфигов.
    Свои надо создавать в /etc/systemd/system где они будут иметь больший приоритет и оверрайдить вендор-дефоулт


    1. ivashkevitch
      10.03.2016 04:41

      Спасибо, исправил.


  1. flyaway
    10.03.2016 03:41
    +5

    Зачем тестировать скорость генерации страницы с внешнего сервера, если можно делать это с локалхоста?
    Задержки, вызванные сетью/ядром можно считать постоянными

    Зачем запускать вдобавок к nginx еще и varnish?
    Последний может иметь смысл на супернагруженом фронтенде, через который проходят терабайты трафика, но не на чахлом VDS с единственным сайтом. Тут одного nginx вполне достаточно, он к тому же сам умеет и SSI, и кеширование.

    Зачем собирать php7 из сырцов и руками изобретать симлинки/стартовые скрипты?
    https://www.digitalocean.com/community/tutorials/how-to-upgrade-to-php-7-on-ubuntu-14-04
    Первая же ссылка в гугле — установка через ppa в пару команд.

    В остальном хорошо, пытливость имеется, остается овладеть матчастью.


    1. ivashkevitch
      10.03.2016 04:54
      -1

      Спасибо за советы, о переносе на локалхост думал, но железо, в частности частота процессора, будет другим, хотя, для того чтобы показать разницу с ESI и без него, вполне подойдёт. Буду разбираться с кэшированием и SSI в nginx. Почему-то он мне показался более сложным в плане этих настроек, по сравнению с Varnish.
      P.S. PHP зато 7.0.4.


      1. flyaway
        10.03.2016 12:27
        +1

        Под тестированием с локалхоста я имел в виду запуск системы нагрузочного тестирования на том же VDS с указанием таргета localhost.

        >PHP зато 7.0.4.

        Заглядывать в этот ppa не стали?
        php7.0 7.0.4-5+deb.sury.org~trusty+1


        1. ivashkevitch
          10.03.2016 12:55

          Понял, спасибо.


      1. xXxSPYxXx
        10.03.2016 14:14

        https://www.dotdeb.org
        Есть какое-то решение проблемы 502, кроме как уход с сокетов?


  1. Evgeny42
    10.03.2016 04:02
    +2

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


    1. ivashkevitch
      10.03.2016 04:45

      А на скольких, по-вашему, он должен падать с таким железом?


  1. OrNix
    10.03.2016 08:35

    Вместо handler sockets посмотрите на стандартный InnoDB Memcache Plugin: https://dev.mysql.com/doc/refman/5.6/en/innodb-memcached-setup.html


  1. nikolayvaganov
    10.03.2016 08:42
    +5

    вместо "make install" использовать checkinstall. Вообще очень странно, что у Вас бекенд fpm висит на TCP/IP вместо unix socket .


    1. nonname
      10.03.2016 11:23
      +2

      Ну и как альтернатива например можно использовать сторонний репозиторий тот же dotdeb, там уже есть пакеты для PHP7, чтобы не собирать их на дохленькой впс.


      1. nikolayvaganov
        10.03.2016 14:14
        +1

        dotdeb для продакшена крайне плох. По опыту — очень нестабильные пакеты у них. По поводу TCP/IP vs unix socket — не видел проектов, чтобы быстрый сокет проигрывал по скорости, стабильности, ресурсоемкости TCP/IP подключению, зато видел сотню висящих соединений в SYN_RECV. И по логике вещей при каждом новом подключении к tcp/ip сокету нужно будет ждать tcp handshake.


        1. xXxSPYxXx
          10.03.2016 14:20

          Есть проблема в php7. Через некоторое время работы php7-fpm начинает выплевывать в Nginx 502 ошибку. Что только не делал чтобы исправить. На своем блоге сейчас пришлось вернуть на php5.6


          1. ivashkevitch
            10.03.2016 14:22

            На TCP пробовали переводить?


            1. xXxSPYxXx
              10.03.2016 14:24

              Нет, TCP хуже сокетов.


          1. nikolayvaganov
            10.03.2016 15:03
            +1

            Можете подсказать, для каких целей требуется использовать версии php не из репозиториев системы? Понятно, что для хайлоад проектов требуется собирать ПО с нужными опциями и отключать ненужные, но для среднего блога я не вижу разницы в использовании совсем новых сырых релизов, при том, что в новом новых версиях баги и дырки находят значительно чаще, чем в старых. Тут можно ( если можно ) сравнить с сопровождением Linux систем в целом. Выходит новый релиз, ждешь год для обкатки, выявления багов и уязвимостей, потом начинаешь внедрять для проектов.


          1. Sannis
            11.03.2016 03:24

            Не пробовали чем-нибудь вроде strace смотреть чем в этот момент php-fpm занимается?


    1. ivashkevitch
      11.03.2016 10:36

      Перевёл на сокет, проблему с 502 ошибкой решил добавлением net.core.somaxconn = 65535 в /etc/sysctl.conf


      1. nikolayvaganov
        11.03.2016 12:18

        Также крайне рекомендую отдельные сайты разносить под отдельных пользователей :

        1. В плане безопасности.
          Ломают один сайт — вешают шелл, шелл запускается от пользователя, а не от www-data.

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

        3. В плане работы с файлами
          Стандартный umask 755, соответственно, файлы созданные из php будут иметь владельца www-data с разрешениями 755 и при работе из под пользователя, владельца сайта ( допустим через ftp ) пользователь будет не в состоянии изменять файл.
          user = user
          group = user
          listen = /var/run/site.ru.sock
          listen.owner = user
          listen.group = www-data
          listen.mode = 0660


  1. Dmi3yy
    10.03.2016 09:30

    Теперь я знаю еще 1 причину почему люблю MODX Evo ;) он без оптимизации работает на уровне очень хорошо оптимизированного WP еще и на VPS))
    Несомненно популярность WP и количество готовых решений и тем вне конкуренции но скорость работы крайне важный показатель.


  1. Graid
    10.03.2016 11:17
    +1

    ivashkevitch, оптимизацией пагинатора при большом кол-ве постов не занимались? Насколько видел очень узкое место из коробки.


    1. ivashkevitch
      10.03.2016 11:43

      Нет, но спасибо за информацию.


  1. jehy
    10.03.2016 11:42
    +4

    Очень странный стресс тест. Вроде как вы выбираете базу данных по тому, когда у вас упадёт сервер, а потом при обновлении PHP максимальная нагрузка увеличивается со 150 до 200 пользователей. Возможно, потому что база у вас не является узким местом? И вообще, причина падения абсолютно непонятна. У вас закончилась память, своп, процессор, или что? Не вижу ни одного мониторинга, вижу желание потыкать пальчиком и посмотреть, вдруг что случится. Настройка wordpress под highload это весьма интересная тема, но если вы претендуете на полноценное исследование, что анализируйте и делайте выводы, а не "оно упало". Пока что на основании ваших опытов выводы можно сделать совершенно разные. Очень жду полноценного исследования, серьёзно.

    Кстати, отдельной интересной темой является оптимизация wordpress под SSL и HTTP2 — тоже можно посмотреть. У меня по моей инсталляции создаётся ощущение, что возникают новые интересные грабли. А wordpress без SSL в наше время — это уже не очень хорошо.

    К слову насчёт использования TCP\IP вместо сокетов — насколько я помню, ошибка при использовании сокетов возникает в случае, если исчерпан системный лимит на количество открытых файлов. Просто увеличьте его. Сокеты всё же немного быстрее.


    1. xXxSPYxXx
      10.03.2016 14:23

      "К слову насчёт использования TCP\IP вместо сокетов — насколько я помню, ошибка при использовании сокетов возникает в случае, если исчерпан системный лимит на количество открытых файлов. Просто увеличьте его. Сокеты всё же немного быстрее."
      Поделитесь подробным мануалом или ссылкой.


      1. jehy
        10.03.2016 15:03
        +2

        Интересно — начал искать готовый развёрнутый ответ и не нашёл. Видимо, для части людей очевидно, для остальных — сложно.
        В общем — сокеты быстрее, так как избавляют от TCP\IP оверхеда.

        К сожалению, большинство "профессиональных" советов касательно настройки говорят "ну, тут можно покопаться в системных настройках для оптимизации сокетов, но боже мой — это так сложно — давайте лучше настроим по TCP\IP". И все радостно так делают. Вся поисковая выдача этим забита.

        На самом деле, можно открыть хоть 600.000 коннектов по сокетам. Вы упираетесь только в максимальное количество открытых файловых дескрипторов, которое установлено у вас на системе.Его легко можно увеличить (то самое страшное действие, которого никто не делает).

        Так что в итоге — да, сокеты быстрее, легче и секьюрнее, хотя выигрыш получается довольно небольшой. Для 90% пользователей проще настроить TCP\IP и забыть. Но если уж мы пишем серьёзную статью про оптимизацию, то говорить в ней о том, что нужно обязательно использовать TCP\IP — это некорректно. Кстати, это тоже вполне себе материал для следующей статьи на эту тему — нормально настроить сокеты и посмотреть, как это повлияет на производительность блога.


        1. ivashkevitch
          10.03.2016 15:06

          Спасибо.


      1. ivashkevitch
        11.03.2016 10:34
        +1

        Только что решил проблему. net.core.somaxconn = 65535 в /etc/sysctl.conf
        После перехода на сокеты сайт стал держать 276 юзера вместо 200, максимальное время ответа при этом сократилось до 7 секунд, а было 13.
        В Debain 8 x64 по умолчанию это значение было 128. Обновил статью.


  1. as3k
    12.03.2016 09:17

    Убил ночь на эксперименты, арендовал VPS с такими же характеристиками, поднял apache+php7+varnish+marinadb, вообщем — все, что было в обоих статьях, результат оказался скорее плачевным, мой сайт на WP статику отдает на 2 секунды дольше, чем самый дешевый хостинг…
    Связка работает, в какую сторону копать — не знаю. Перепробовал все, и сжатие, и без varnish, короче беда)


    1. ivashkevitch
      12.03.2016 11:25

      Видимо, Varnish статику не кеширует и отдачей занимается Apache. Напишите в личку, посмотрим.


    1. jehy
      12.03.2016 11:31

      PHP подключали в виде php-fpm? Но вообще, nginx заметно бодрее apache.


  1. zm_llill
    17.03.2016 13:28

    А зачем компилировали PHP 7, не проще было подключить репозиторий и с него установить? На Убунту я себе на тестовом сервере так и сделал, например.