Рано или поздно у каждого разработчика проекта с ростом посетителей возникает вопрос, как ускорить загрузку сайта, каким образом решить проблему с его быстродействием. Наиболее эффективным решением повысить производительность сайта и ускорить загрузку страниц будет использование кэширующего прокси-сервера Varnish с поддержкой ESI. На странице документации можно найти методы установки для популярных Linux-дистрибутивов или скачать исходный код, поскольку это полностью бесплатный продукт и выпускается под BSD-лицензией.
Установка и настройка программного обеспечения Varnish
На момент написания статьи актуальной версией была 4.0.3, пример установки для Ubuntu 14.04 (Trusty Tahr):
$ apt-get install apt-transport-https $ curl https://repo.varnish-cache.org/GPG-key.txt |apt-key add - $ echo"deb https://repo.varnish-cache.org/ubuntu/ trusty varnish-4.0">>/etc/apt/sources.list.d/varnish-cache.list $ apt-get update $ apt-get install varnish
После установки появится список доступных утилит:
- varnishadm — для администрирования varnishd-демона;
- varnishhist — гистограмма запросов;
- varnishlog — журнал работы;
- varnishncsa — отражает логи в Apache / NCSA формате;
- varnishstat — статистика использования кэша;
- varnishtest — тестовая утилита;
- varnishtop — топовая статистика по использованию кэша.
Varnish для конфигурации использует предметно-ориентированный язык программирования VCL (Varnish configuration Language). Файл конфигурации /etc/varnish/default.vcl
:
vcl 4.0; backend default_server { .host="127.0.0.1"; .port="8080";} sub vcl_recv {if(req.http.Cookie){ set req.http.Cookie=";"+ req.http.Cookie; set req.http.Cookie= regsuball(req.http.Cookie, "; +", ";"); set req.http.Cookie= regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1="); set req.http.Cookie= regsuball(req.http.Cookie, ";[^ ][^;]*", ""); set req.http.Cookie= regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");if(req.http.Cookie==""){ unset req.http.Cookie;}}// Add a Surrogate-Capability header to announce ESI support. set req.http.Surrogate-Capability ="abc=ESI/1.0";return(hash);} sub vcl_backend_response {// Check for ESI acknowledgement and remove Surrogate-Control headerif(beresp.http.Surrogate-Control ~ "ESI/1.0"){ unset beresp.http.Surrogate-Control; set beresp.do_esi=true;}}
default_server
— используется для настройки бэкенд-серверов, в данном случае прописан один «default_server» — symfony-приложение на порту 8080, varnish по умолчанию слушает 6081 порт, его можно изменить в файле конфигурации демона /etc/default/varnish
.
vcl_recv
— вызывается в начале запроса.
vcl_backend_response
— используется при получении ответа из бэкенд-сервера.
Стоит отметить, что HTTP-кэширование работает только с «безопасными» методами, такими как GET, HEAD. Под «безопасностью» имеется в виду, что эти методы не будут менять состояние данных (редактирование, удаление). Никогда не стоит кэшировать методы, которые меняют состояние (POST, DELETE, PUT, PATH), так как запросы могут не достичь функционального уровня выполнения на бэкенде и будут обработаны на уровне кэша.
На этом этапе конфигурация Varnish закончена.
Настройка кэширования с помощью прокси-сервера Varnish для сайтов, разработанных на PHP фреймворке Symfony2
Если вы перейдете по адресу https://localhost:6081/, то у вас должна отобразиться следующая информация:
Так как на данном этапе еще не настроено Symfony2-приложение для работы с прокси-сервером, отображается 503 ошибка. На момент написания статьи актуальной была версия 2.7, поэтому конфигурация будет соответствовать данной версии. С процессом установки можно ознакомиться в нашей предыдущей статье. Для начала нужно переопределить некоторые параметры конфигурации фреймворка в app/config/config.yml
:
framework: esi: { enabled: true} trusted_proxies: ['127.0.0.1'] fragments: { path: /_fragment }
Из описания параметров должно быть понятно их назначение. Первый esi {enabled: true}
активирует поддержку esi
, trusted_proxies
— список прокси-серверов, fragments
— роут для генерации фрагментов контента. После этого нужно проставить параметры кэша для соответствующих экшенов:
<?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Response; class DefaultController extends Controller {/** * @Route("/app/example", name="homepage") * @Cache(smaxage=30) */publicfunction indexAction(){return$this->render('default/index.html.twig');}}
Для более удобной и массовой конфигурации можно использовать FOSHttpCacheBundle. Теперь, если перейти по адресу https://localhost:6081/, у вас должен отразиться контент тестового контроллера с заголовками в ответе:
Age: 7 Cache-Control: public, s-maxage=30
Age
— время в секундах, возраст кэша.
Cache-Control: public
указывает на то, что этот контент является публичным (его могут кэшировать публичные proxy-сервера, и он является общим для всех пользователей); s-maxage
указывает время жизни в секундах. В альтернативном варианте, если заменить @Cache(smaxage=30)
на @Cache(maxage=30)
, private
будет указывать на то, что кэшировать ответы смогут только частные сервера (браузеры).
Для кеширования динамических блоков контента нужно использовать ESI. К примеру общая схема работы:
Сначала пользователь посылает запрос на получение ресурса. Если в теле ответа содержатся специальные теги <esi:include src”/...” />
, кэширующий сервер запрашивает бэкенд для получения дополнительных блоков контента. В Symfony2 реализована Twig-функция render_esi
для отображения esi-тегов, входящим параметром может быть объект типа ControllerReference
(например, {{render_esi(controller('AppBundle:News:latest', { 'maxPerPage': 5 })) }}
), или URL-ссылки ({{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
). После добавления esi блока шаблон app/Resources/views/default/index.html.twig
будет выглядеть следующим образом:
{%extends'base.html.twig'%} {% block body %} Homepage.{{ render_esi(controller('AppBundle:Default:esiFragment'))}}{% endblock %}
Также в контролере нужно добавить екшен esiFragment
:
/** * @Cache(smaxage=15) */publicfunction esiFragmentAction(){returnnew Response('esi fragment');}
Результаты
В данном случае основной блок indexAction
будет кэшироваться 30 секунд, а дополнительный esiFragmentAction
— 15 секунд. Для сравнения результатов проведем небольшой тест.
С Varnish $ ab -c 10 -n 1000 https://localhost:6081/app/example
И без его использования $ ab -c 10 -n 1000 https://localhost:8080/app/example
Как видно из результатов, разница в приросте производительности 9197 против 143 запросов, то есть примерно в 65 раз быстрее. Таким образом, с помощью прокси varnish мы можем значительно ускорить высоконагруженные сайты на Symfony2.