Познавательный блог | Мобильная врсия |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
СтраницыПромо |
Оптимизация сайта на WordPressВашему вниманию представляется история о том, как мы оптимизировали свой сайт. Сайт работает на движке WordPress (на этой фразе большинство читателей должны поморщиться, зная, как обстоят дела у WordPress со скоростью). Однако все-таки у нас получилось, и сайт стал летать. Сразу скажу, что меня вряд ли можно считать профессионалом по серверной оптимизации, однако то, чего удалось достичь, меня сильно радует. Также, был получен бесценный опыт, которым я хочу поделиться с читателями Хабра. ПоехалиМы начали с исследования причин торможения сайта. Лично мне больше всего нравится сервис LoadImpact. Не так давно у него появились новые фичи и новый интерфейс, и сам сервис стал называться «LoadImpact 2.0». Все это сказано не рекламы ради — сервис действительно хорош. Также потестировать сайт можно с помощью аналогичных сервисов — Pingdom Full Page Test и Google PageSpeed. С помощью Load Impact Page Analyzer мы обнаружили большое значение параметра Time to first byte (4 секунды), который рассказал нам о том, что PHP-скрипты, формирующие страницу, выполняются слишком долго. Нужно было искать самые тормозные компоненты сайта. Добавив в шаблон footer.php строчку
Решили сделать профилирование всего PHP-кода в шаблоне. Мы не стали заморачиваться с Xdebug + Performance Testing (для этого нужно было ставить кучу стороннего софта), а провели простое самописное профилирование вида:
Каждая из этих проблем при ближайшем рассмотрении вызывает эталонный facepalm. Сейчас расскажу о каждой из них. РазСамым большим тормозом в нашем сайте оказался модуль геотаргетированной рекламы. Суть ее в том, что на сайте показывается два типа баннеров — один отображается у посетителей из Санкт-Петербурга, другой — у всех остальных. Углубившись в модуль, я обнаружил вот такой, простите, говнокод: if ($ip_part) { for ($y1=$ip_part[0]['start'];$y1<=$ip_part[0]['end'];$y1++) for ($y2=$ip_part[1]['start'];$y2<=$ip_part[1]['end'];$y2++) for ($y3=$ip_part[2]['start'];$y3<=$ip_part[2]['end'];$y3++) for ($y4=$ip_part[3]['start'];$y4<=$ip_part[3]['end'];$y4++) { $ip[] = $y1.".".$y2.".".$y3.".".$y4; if ($ip_addr == $y1.".".$y2.".".$y3.".".$y4) return true; } } В этот момент мне даже стало немного жалко процессор, который все это время выполнял пустую работу… Исходные диапазоны IP-адресов Санкт-Петербурга задавались заранее в массиве $ip_part. Как видно из этого куска кода, при каждом обращении к функции зачем-то создавался динамический массив $ip и заполнялся строковыми (!) данными с IP-адресами, тупейшим образом через 4 вложенных цикла. В каждой итерации цикла IP-адрес посетителя сравнивался построчно (!) с получившейся строкой IP-адреса. Пофиксить это было просто: $ip_tmp = explode(".",$ip_addr); if ( $ip_part ) if ( ($ip_part[0]['start'] <= $ip_tmp[0]) and ($ip_tmp[0] <= $ip_part[0]['end']) ) if ( ($ip_part[1]['start'] <= $ip_tmp[1]) and ($ip_tmp[1] <= $ip_part[1]['end']) ) if ( ($ip_part[2]['start'] <= $ip_tmp[2]) and ($ip_tmp[2] <= $ip_part[2]['end']) ) if ( ($ip_part[3]['start'] <= $ip_tmp[3]) and ($ip_tmp[3] <= $ip_part[3]['end']) ) return true; Не отрицаю, что этот код можно переписать и более оптимально, однако и после этой правки сайт заработал на 2 секунды быстрее! ДваВторой плагин под названием Embedded Stock Data, который у нас на страничке показывал котировки Apple. Обнаружилось, что этот гад при каждой загрузке страницы ходил на другой сайт (!) через CURL. Естественно, это сильно увеличивало время загрузки страницы. Решили проблему естественным образом — на сервере раз в минуту по крону вызывался скрипт, который скачивал нужную котировку и сохранял ее в специальный файл. Далее мы этот файл инклудили. Это дало нам около 500 мс. ТриБоковые блоки «Обзор приложений» и «Самое интересное» также выполнялись ощутимое время. Заглянув внутрь шаблона (sidebar.php), я увидел классическую ошибку начинающих веб-программистов под названием «ORDER BY RAND()». Подробнее о сути этой проблемы можно, например, почитать тут. Для интересующихся, был вот такой код: $args=array( 'caller_get_posts'=>'1', 'post__not_in' => $sticky, 'cat'=>'75', 'fields'=>'ids', 'post_per_page'=>'20', 'orderby'=>'ID', ); $sk_count = new WP_Query($args); $args=array( 'caller_get_posts'=>'1', 'post__not_in' => $sticky, 'showposts'=>'4', 'cat'=>'75', <b> 'orderby'=>'rand'</b> ); $sk_posts = new WP_Query($args); В нем мы заменили выделенную строку на 'post__in'=>array_rand(array_flip($sk_count->posts),4) Суть решения — мы предварительно выбираем 20 самых свежих записей, потом выбираем из этих 20 записей случайные 4 штуки, а затем уже выбираем из базы посты с этими номерами. На этом мы сэкономили еще полсекунды. Переезд на ClodoНаш железный сервер изначально хостил большое количество сайтов, но самым главным по трафику и по значению был AppleInsider.ru. Нам захотелось обезопасить этот сайт от соседства с другими сайтами, и было решено перенести его в облако компании Clodo. Главным преимуществом Clodo можно назвать «эластичность» тарификации — сколько ресурсов используешь, столько и платишь. Положа руку на сердце, также хочется отметить техподдержку компании, которая даже в не-VIP варианте вела себя корректно, отвечала на все наши каверзные вопросы и помогала собственно в настройке. «Под шумок» с переносом были сделаны некоторые изменения, например мы убрали насовсем тяжелый веб-сервер Apache, а вместо него сделали связку nginx + php-fpm, которая кушает меньше памяти. Nginx работал по обычной схеме — отдавал статику, а все остальное перенаправлялось к скрипту index.php, в котором идет дальнейшая WordPress-логика. Чтобы не отнимать ресурсы железа, отведенного под веб-сервер nginx + php-fpm, мы вынесли сервер базы данных MySQL на отдельный сервер, связав его с Web-сервером посредством локальной сети. Также мы настроили два отдельных «облачных» сервера — для нашего подкаста AppleInsider.ru и Яблочного огрызка и для текстовых онлайн-трансляций. О них мы расскажем подробнее в следующий раз. Переход на Clodo ненамного сократил время генерации страницы, зато это обезопасило сам сайт. WP Super Cache и асинхронная рекламаНа этом этапе страница у нас формировалась примерно за 1 секунду. Уже неплохо, но нам было мало, ведь оставалось еще несколько моментов, которые можно было оптимизировать. Например, воспользоваться плагином для кеширования страниц WordPress'а под названием WP Super Cache. Работает как обычный кеш — пользователь заходит на страницу, она при этом генерируется и кладется в каталог /wp-content/cache/supercache. Следующему посетителю будет отдаваться уже статический заранее сформированный HTML-файл. Казалось бы, все супер — сайт летает… Однако, как вскоре обнаружилось, использование кеширования поломало геотаргетированную рекламу. Проверка на вхождение адреса посетителя в диапазон адресов Санкт-Петербурга выполнялось на уровне PHP, т.е. уже за кешем. Соответственно, результат этой проверки «навеки» оставался в кеше. А нам нужно было все-таки разграничивать показ баннеров. Решили эту задачу следующим образом — в шаблоне странички в нужных местах были расставлены Javascipt-инструкции, указывающие на то, что в этот div-блок нужно подгрузить контент с определенного URL-а. Таким образом, пользователь беспрепятственно получает закешированную страницу с Javascript-инструкциями, и уже после появления первых букв начинают загружаться рекламные и прочие структурные блоки сайта, хранящимся по отдельным специальным URL-адресам. Визуально это происходит очень быстро. Так нам удалось достичь двух целей — во-первых, за счет кеширования значительно ускорился сайт, и во-вторых, получилась корректно работающая геотаргетированная реклама. ClodoStorageПоследний шаг, который мы сделали в оптимизации — перенесли статический контент (картинки, стили CSS, Javascript) на CloudStorage. Подробнее о нем можно почитать тут. В двух словах — это Content Delivery Network от компании Clodo, а по-русски — очень быстрый сервер, специально заточенный для раздачи статики. Интеграция с WordPress состоит в правильной настройке двух плагинов — CDN Sync Tool (для Clodo нужно использовать специальную сборку библиотеки PHP Cloud Files API) и WP Super Cache. Первый плагин, CDN Sync Tool, занимается тем, что заливает контент WordPress на хранилище, производя попутно с этим самым контентом какие-нибудь действия. При наличии плагина WP Minify он может сжимать CSS- и JS-файлы. Также он может налету сжимать загружаемые картинки. При этом используется библиотека GD, для нас наиболее приемлемым оказался уровень сжатия GD Compression Level = 2 — выражаясь в терминах других программ, это 90% качества. Самое главное, конечно, это то, что при стандартной загрузке картинок авторами, они стали автоматически заливаться на хранилище. Для статики мы завели отдельный поддомен static.appleinsider.ru, а первоначальную загрузку контента проделывали с помощью CyberDuck — это довольно удобная программа для работы с FTP, SFTP, WebDAV, Cloud Files, Google Docs и Amazon S3. Вот скриншот плагина CDN Sync Tool, тюнингованного под Clodo (кликабельно): Второй плагин, о котором мы уже упоминали, WP Super Cache, поступает хитро — он берет сформированную страницу и заменяет в ней все URL-ы для статических файлов на URL в хранилище. Например, https://www.appleinsider.ru/Angry-Birds.jpg станет https://static.appleinsider.ru/Angry-Birds.jpg ИтогиСейчас при отсутствии страницы в кеше страница загружается за 1.25 секунды, при наличии в кеше — 250 мс — и это с учетом всех 4 факторов — DNS Lookup + Connection + Time to first + Download. Я считаю, что это потрясающий результат! В заключение приведу сухие факты — статистику использования ресурсов и финансовые затраты. За неделю работы 23.11.2011 — 30.11.2011: Web
DB
CloudStorage
Всего за неделю работы сайта — 1771.16 руб. Довольно много, но стабильность и довольные пользователи стоят того :-) |
Поиск по сайтуСтатистикаМета |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Разработка и дизайн — StvPromo.ru |
Написать комментарий