Работа с включениями крайней стороны
Дата обновления перевода 2024-07-25
Работа с включениями крайней стороны
Шлюзовые кеши - это отличный способ повысить производительность вашего вебсайта. Но они имеют одно ограничение: они могут кешировать только страницу целиком. Если ваши страницы содержат динамические секции, такие как имя пользователя или корзину покупок, то вам не повезло. К счастью, Symfony предоставляет решение для таких случаев, основанное на технологии под названием ESI, или Включения крайней стороны. Акамаи написал эту спецификацию в 2001 году, и она позволяет определённым частям страницы иметь другие кеширующие стратегии, чем главная страница.
Спецификация ESI описывает теги, которые вы можете встраивать в ваши страницы, чтобы
коммуницировать со шлюзовым кешем. Только один тег реализован в Symfony, include
,
так как он единственный полезен вне контекста Akamai:
1 2 3 4 5 6 7 8 9 10 11
<!DOCTYPE html>
<html>
<body>
<!-- ... некоторое содержание -->
<!-- Встроить содержание другой страницы здесь -->
<esi:include src="http://..."/>
<!-- ... больше содержания -->
</body>
</html>
Note
Отметьте в примере, что каждый тег ESI требует полностью квалифицированного URL. ESI-тег представляет фрагмент страницы, который можно получить через заданный URL.
Когда обрабатывается запрос, кеширующий шлюз вызывает целую страницу из кеша или запрашивает её из приложения выходного буфера. Если ответсодержит один или более ESI-тегов, то они обрабатываются таким же образом. Другими словами, кеширующий шлюз либо извлекает включённый фрагмент страницы из его кеша, или запрашивает фрагмент страницы из приложения выходного буфера опять. Когда будут разрешены все ESI-теги, кеширующий шлюз объеиняет каждый из них с главной страницей и отправляет окончательное содержимое клиенту.
Всё это происходит прозрачно на уровне кеширующего шлюза (т.е. вне вашего приложения). Как вы увидите, если вы решите воспользоваться преимуществами ESI-тегов, Symfony сделает професс включения их почти не требущим усилий.
Использование ESI в Symfony
Для начала, чтобы использовать ESI, включите их в конфигурации вашего приложения:
1 2 3 4
# config/packages/framework.yaml
framework:
# ...
esi: true
Теперь, представьте, что у вас есть страница, которая относительно статична, кроме бегущей новостной строки внизу содержимого. С ESI вы можете кешировать эту бегущую строку независимо от остальной страницы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/DefaultController.php
namespace App\Controller;
// ...
class DefaultController extends AbstractController
{
public function about(): Response
{
$response = $this->render('static/about.html.twig');
$response->setPublic();
$response->setMaxAge(600);
return $response;
}
}
В этом примере, ответ помечен, как публичный, чтобы сделать всю страницу
кешируемой для всех запросов, с жизненным циклом в десять минут.
Далее, включите новостной тикер в шаблон, встроив действие. Это делается
через помощник render()
(чтобы узнать больше, прочтите, как
встраивать контроллеры в шаблоны ).
Так как встроенное содержимое поступает с другой страницы (или контроллера,
раз на то пошло), Symfony использует стандартного помощника render
, чтобы
сконфигурировать ESI-теги:
1 2 3 4 5 6 7
{# templates/static/about.html.twig #} {# вы можете использовать ссылку на контроллер #} {{ render_esi(controller('App\Controller\NewsController::latest', { 'maxPerPage': 5 })) }} {# ... или на URL #} {{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
Используя рендерер esi
(через функцию Twig render_esi()
), вы сообщаете
Symfony, что действие должно быть отображено, как ESI-тег. Вы можете недоумевать,
почему вам может захотеться использовать помощника вместо того, чтобы просто
написать ESI-тег самостоятельно. Это потому, что использование помощника заставляет
ваше приложение работать ровно, даже если не установлен кеширующий шлюз.
Tip
Как вы увидите ниже, переменная maxPerPage
, которую вы передаёте,
доступна в качестве аргумента вашего контроллера (т.е. $maxPerPage
).
Переменные, переданные через render_esi
также становятся частью
ключа кеша, чтобы у вас были уникальные кеши для каждой комбинации
переменных и значений.
При использовании функции render()
по умолчанию (или установке рендерера как
inline
), Symfony объединяет включённое содержание страницы с главной страницей
до отправки ответа клиенту. Но если вы используете рендерер esi
(т.е. вызываете
render_esi()
) и если Symfony определяет, что она имеет дело со шлюзовым кешем,
поддерживающим ESI, она сгенерирует тег включения ESI. Но если шлюзового кеша нет,
или если он не поддерживает ESI, то Symfony просто объединит включённое содержание
страницы с главной, так же, как было бы, если бы вы использовали render()
.
Note
Symfony считает, что кеш шлюза поддерживает ESI, если его запрос включает в
себя HTTP-заголовок Surrogate-Capability
, и значение этого заголовка содержит
где-либо строку ESI/1.0
.
Встроенное действие теперь может указать свои собственные правила кеширования, полностью не зависящие от главной страницы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Controller/NewsController.php
namespace App\Controller;
use Symfony\Component\HttpKernel\Attribute\Cache;
// ...
class NewsController extends AbstractController
{
#[Cache(smaxage: 60)]
public function latest(int $maxPerPage): Response
{
// ...
}
}
В этом примере, встроенное действие тоже кешируется публично, так как содержание одинаково для всех запросов. Однако, в других случаях, вам может понадобиться сделать этот ответ непубличным и даже некешируемым, в зависимости от ваших потребностей.
Составив весь вышенаписанный код, с ESI, кеш всей страницы будет валиден в течение 600 секунд, но кеш компонента новостей будет длиться только 60 секунд.
При использовании ссылки на контроллер, ESI-тег должен ссылаться на встроенное действие, как доступный URL, чтобы кеширующий шлюз могу извлечь его независимо от остальной страницы. Symfony заботится о том, чтобы сгенерировать уникальный URL для любой ссылки контроллера, может правильно проложить маршруты, благодаря FragmentListener, который должен быть включен в вашу конфигурацию:
1 2 3 4
# config/packages/framework.yaml
framework:
# ...
fragments: { path: /_fragment }
Одним замечательным преимуществом ESI-рендерера является то, что вы можете сделать ваши приложения настолько динамичными, насколько это нужно, и одновременно с этим, обращаться к приложению настолько редко, насколько это возможно.
Caution
Слушатель фрагментов отвечает только на подписанные запросы. Запросы подписываются
только при использовании рендерера фрагмента и функции Twig render_esi
.
Помощник render_esi
поддерживает две другие полезные опции:
alt
-
Используется как атрибут
alt
в ESI-теге, который позволяет вам указывать альтернативный URL для использования, если нельзя найтиsrc
. ignore_errors
-
Если установлен как "true", к ESI будет добавлен атрибут
onerror
со значениемcontinue
, обозначающим, что в случае неудачи, кеширующий шлюз просто тихо удалит ESI-тег. absolute_uri
-
Если установлен как "true", будет сгенерирован абсолютный URI. По умолчанию:
false