Как структурировать приложение Symfony2 с помощью ESI?
В новом проекте с большим объемом трафика мы думаем о том, как структурировать наше приложение Symfony2, чтобы использовать преимущества кэшей и быть готовыми к более агрессивным действиям в будущем. Я хотел бы знать ваше мнение.
Допустим, пользователь запрашивает у страницы список мест. Эта страница имеет:
- list
- common data (title, author, description)
- user data (the user likes the list + other data)
- first 20 places
- common data (title, photo of each place)
- user data (the rates of the user for those places)
HTML может быть таким:
<html>...
<body>
<header>
...
<!-- Embed the top user menu -->
<esi:include src="http://example.com/profile/menu" />
...
</header>
<content>
...
common data of the list
...
<!-- Embed the common data of the first 20 places, the same for everyone -->
<esi:include src="http://example.com/lists/17/places" />
...
<!-- Embed the user data of the list (used in JS) -->
<esi:include src="http://example.com/lists/17/user" />
...
<!-- Embed the user data of the list of places (used in JS) -->
<esi:include src="http://example.com/lists/17/places/user" />
...
</content>
</body>
</html>
HTML будет кэшироваться на шлюзе (Symfony или Varnish). Список мест также будет кэшироваться большую часть времени на шлюзе. Запросы пользовательских данных будут вызваны и не будут кэшироваться (по крайней мере, изначально).
Вопросы:
- Как вы относитесь к этой структуре?
- Если пользователь является анонимным, могу ли я избежать включения esi для пользовательских данных? Также, если у меня есть печенье для анон пользователя? Как?
- Имеет ли смысл esi-include для пользовательского меню?
- Или мы должны забыть о ESI и всегда проходить через контроллер (например, кешировать представленное представление общих данных)?
- Должны ли мы переместить 2 ESI-запроса, которые запрашивают данные пользователя как AJAX-вызовы, вместо ожидания на сервере?
- Это хороший подход к масштабированию, если нам нужно сделать это быстро? Что будет лучше?
большое спасибо!
1 ответ
Мы использовали Varnish на одном сайте для кэширования целых страниц, и я использую Symfony2 в течение нескольких лет, но имейте в виду, что я не использовал Varnish + Symfony2 + ESI в любой производственной среде.
Я думаю, что основная идея в порядке. Если меню на многих страницах одинаковое, а список мест на многих страницах одинаковый, вы получаете общий контент, кэшированный с помощью обратного кэша Varnish или Symfony. Поскольку Varnish обычно хранит кэш в памяти, вы получаете контент быстрее и вам не нужно вызывать код рендеринга и запросов к БД при каждом запросе.
Сложнее всего сделать кэширование этих ESI-запросов, если пользователь вошел в систему. Как я знаю, в конфигурации Varnish по умолчанию запросы с Cookie в них никогда не кэшируются. Если вы склонны передавать файлы cookie на запросы ESI, эти ответы ESI не будут передаваться пользователям.
Вы можете попробовать создать некоторые правила из URL, но если вы используете стандартные помощники Symfony по веткам, сгенерированные URL-адреса являются /_internal/..., поэтому может быть трудно различить публичные и частные.
Также вы можете настроить, чтобы всегда игнорировать любые куки, если
Cache-Control: public
передается. Это делается по умолчанию в Symfony:if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) { $response->setPrivate(true); }
Как видно из кода, если у вас есть
public
директива, ответ никогда не будет частным.Я не нашел, как Varnish обрабатывает эту директиву - насколько я понимаю, она не кэширует запросы, которые имеют cookie по умолчанию. Поэтому я думаю, что вам нужно настроить конфигурацию для достижения этой цели.
Если главная страница также должна быть кэширована, я не вижу, как можно пропустить включения.
Я предполагаю, что JS требуется для ваших зарегистрированных пользователей (не для поисковых роботов), поэтому я бы предложил использовать Javascript, чтобы отличать загрузку пользовательских данных.
Javascript код может посмотреть, есть ли у пользователя cookie
session-id
и т.д. и сделать запрос на получение данных только в этом случае. Также может быть хорошей идеей установить какой-нибудь другой файл cookie, например_loggedin
чтобы Javascript-код не мог получить идентификатор сессии.Не вошедшие в систему пользователи могут также иметь некоторые данные в куки, например
_likedPost:1,2,132
, Javascript может получить этот файл cookie и внести некоторые исправления в HTML, даже не делая дополнительного запроса.Как и в случае с этими файлами cookie: мы отделили файлы cookie только для JS от файлов cookie приложения. Мы сделали это по какой-то схеме, например
_\w
для печенья JS. Затем мы настроили конфигурацию Varnish, чтобы разделить заголовок Cookie и удалить эти файлы cookie только для JS. Затем, если не осталось других файлов cookie, ответ передается всем. Приложение (Symfony) не получает эти куки, так как они удалены.Я думаю, что так и есть, если он одинаков на каждой странице.
Я думаю, что ESI хорош, так как Varnish может хранить кеш в памяти. Так что, возможно, он даже не будет делать какие-либо запросы на ваш жесткий диск для содержимого. Поскольку кэш вашего контроллера может также находиться в памяти, я думаю, что Varnish будет искать кэш быстрее, чем фреймворк Symfony со всей маршрутизацией, PHP-кодом, инициализацией сервисов и т. Д.
Это зависит, но я думаю, что это может быть лучше. Имейте в виду, что кэши живут разными жизнями. Например, если ваш список мест кэшируется в течение 2 часов, по истечении этого времени места могут измениться - некоторые новые элементы являются новыми в списке, а некоторые отсутствуют. Ваш список для пользователя все еще старый (кэшированный), но вы предоставляете данные пользователя о новом списке - некоторые данные не нужны, а некоторые отсутствуют.
Возможно, лучше подходить для загрузки загружаемых мест с помощью JavaScript, например, для поиска некоторого атрибута HTML, такого как
data-list-item-id
и затем сделайте запрос ajax, запрашивая данные об этих элементах. В этом случае ваши пользовательские данные будут синхронизированы с текущим кэшированным списком, и вы можете сделать 1 ajax-запрос для обоих списков вместо 2.Если аннулирование кэша (запросы PURGE) не используются, то весь HTTP-кэш действительно хорош для масштабирования. Вы можете масштабировать приложение до нескольких серверов и настроить Varnish для случайного вызова по некоторому правилу или для использования одного из них в качестве отказоустойчивого. Если пропускная способность все еще слишком велика, вы всегда можете изменить время ожидания кэша и другую конфигурацию.