
Рано чи пізно, під час залучення нових користувачів, кожен проект стикається з проблемою повільного часу відповіді сервера. Найефективнішим рішенням для покращення продуктивності вебсайту (ця стаття стосується високо завантажених сайтів на Symfony2) є використання проксі-сервера кешування Varnish з підтримкою Edge Side Includes (ESI).
Зачекайте. Що таке Varnish? Це, по суті, HTTP-акселератор, розроблений для динамічних вебсайтів з високим навантаженням. Що таке Symfony2? Це PHP-фреймворк для роботи з проксі-сервером.
У документації розробник може знайти методи встановлення Varnish для популярних дистрибутивів Linux або завантажити вихідний код, оскільки Varnish є повністю безкоштовним програмним забезпеченням, доступним під ліцензією 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;}}// Додати заголовок Surrogate-Capability для оголошення підтримки ESI. set req.http.Surrogate-Capability ="abc=ESI/1.0";return(hash);} sub vcl_backend_response {// Перевірити визнання ESI та видалити заголовок Surrogate-Controlif(beresp.http.Surrogate-Control ~ "ESI/1.0"){ unset beresp.http.Surrogate-Control; set beresp.do_esi=true;}}
default_server
використовується для налаштування бекенд-серверів. У цьому випадку є один «дослівний сервер» — додаток Symfony на порту 8080. За замовчуванням Varnish використовує порт 6081. Це можна налаштувати у файлі конфігурації його демона «/etc/default/varnish».
vcl_recv
викликається на початку запиту.
vcl_backend_response
використовується під час отримання відповіді сервера.
Варто зазначити, що кешування HTTP працює лише з «безпечними» методами, такими як GET, HEAD. «Безпечність» тут означає, що ці методи не змінюють стан даних (редагування, видалення). Ніколи не кешуйте методи, які змінюють стан (POST, DELETE, PUT, PATH), оскільки запити можуть не досягти функціонального виконавчого рівня на бекенді, тому вони будуть оброблені на рівні кешу.
На цьому етапі конфігурація Varnish завершена.
Якщо ви перейдете за посиланням http://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
— це маршрут для генерації фрагментів контенту. Після цього нам потрібно встановити параметри кешу для конкретних дій.
<?phpnamespace 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. Тепер, якщо ви перейдете за посиланням http://localhost:6081/, ви повинні побачити вміст тестового контролера з заголовками у відповіді.
Age: 7 Cache-Control: public, s-maxage=30
Age
— це час в секундах. Це, по суті, вік кешу.
Cache-Control: public
вказує на те, що цей контент доступний публічно (його можуть кешувати публічні проксі-сервери, і він загальний для всіх користувачів), 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 %} Головна сторінка.{{ render_esi(controller('AppBundle:Default:esiFragment'))}}{% endblock %}
Вам також потрібно додати дію esiFragment
до контролера.
/** * @Cache(smaxage=15) */publicfunction esiFragmentAction(){returnnew Response('esi фрагмент');}
У цьому випадку основний фрагмент indexAction
кешується на 30 секунд, а додатковий esiFragmentAction
буде кешуватися на 15 секунд. Щоб порівняти результати, давайте проведемо деякі тести:
З Varnish «$ ab -c 10 -n 1000 http://localhost:6081/app/example»
Цього разу не використовуючи «$ ab -c 10 -n 1000 http://localhost:8080/app/example»
Результати показують, що різниця в зростанні продуктивності становить 9197 до 143 запитів, що є різницею в 65 разів. Як ви можете бачити, Varnish Web Accelerator є досить хорошим способом для балансування навантаження на сервер.