Как ускорить работу сайта, используя кэширующий прокси-сервер Varnish

Как ускорить работу сайта в 50 раз, используя кэширующий прокси-сервер Varnish

Рано или поздно у каждого разработчика проекта с ростом посетителей возникает вопрос, как ускорить загрузку сайта, каким образом решить проблему с его быстродействием. Наиболее эффективным решением повысить производительность сайта и ускорить загрузку страниц будет использование кэширующего прокси-сервера 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 header
    if (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/, то у вас должна отобразиться следующая информация:

Как ускорить работу сайта в 50 раз, используя кэширующий прокси-сервер Varnish

Так как на данном этапе еще не настроено 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)
     */
    public function 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. К примеру общая схема работы:

Как ускорить работу сайта в 50 раз, используя кэширующий прокси-сервер Varnish

Сначала пользователь посылает запрос на получение ресурса. Если в теле ответа содержатся специальные теги <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)
 */
public function esiFragmentAction()
{
    return new Response('esi fragment');
}

Результаты

В данном случае основной блок indexAction будет кэшироваться 30 секунд, а дополнительный esiFragmentAction — 15 секунд. Для сравнения результатов проведем небольшой тест.

С Varnish $ ab -c 10 -n 1000 https://localhost:6081/app/example

Как ускорить работу сайта в 50 раз, используя кэширующий прокси-сервер Varnish

И без его использования $ ab -c 10 -n 1000 https://localhost:8080/app/example

Как ускорить работу сайта в 50 раз, используя кэширующий прокси-сервер Varnish

Как видно из результатов, разница в приросте производительности 9197 против 143 запросов, то есть примерно в 65 раз быстрее. Таким образом, с помощью прокси varnish мы можем значительно ускорить высоконагруженные сайты на Symfony2.