Сети доставки контента (CDN)
CDN кэширует контент на edge-узлах рядом с пользователями, срезая задержку и разгружая origin. Рычаги — cache-control/TTL, hit ratio, push против pull, origin shielding, purge. Знать, что НЕ кэшировать и как работает инвалидация, важнее всего.
Продуктовая команда выкатила «маленькое» изменение цен и пустила его в прод. Клиенты в Европе весь остаток дня видели старые цены; клиенты в Азии — двое суток. Никто не задеплоил баг — новая страница цен несла Cache-Control: max-age=86400, и сотни edge-узлов CDN по всему миру каждый отдавал идеально закэшированную копию, которая была теперь неверна, без указания перепроверить. Тот же edge-кэш, что делал сайт быстрым для всех, прикрепил устаревшую цену перед каждым клиентом, и команда тяжело усвоила: на CDN положить что-то на периметр легко, а забрать обратно — трудная часть. Урок был не «не используй CDN», а что TTL и purge — это осознанное проектное решение, а не дефолт, который можно игнорировать.
Что покупает CDN
Сеть доставки контента (CDN) — это глобально распределённый парк кэш-серверов (edge-узлы, или PoP — points of presence), размещённых близко к пользователям. Когда приходит запрос, ближайший edge-узел либо отдаёт закэшированную копию (попадание в кэш, cache hit), либо берёт с твоего origin (источника), кэширует и отдаёт (промах, cache miss). Он покупает две вещи, которые складываются:
- Меньшую задержку. Пользователь общается с edge-узлом в нескольких миллисекундах, а не с origin, до которого может быть кросс-региональный путь ~150 мс (иерархия задержек из юнита масштабируемости). Физика, не хитрость: меньше расстояние, меньше круговых рейсов.
- Разгрузку origin. Каждое попадание — это запрос, который origin никогда не видит. Сайт с hit ratio 95% шлёт на origin лишь 1 запрос из 20, поэтому парк origin может быть ~20× меньше — и переживает всплески трафика, которые иначе бы его расплавили.
Он также вбирает объёмные атаки и сглаживает всплески, потому что edge-парк огромен, а origin прячется за ним. Трек networking покрывает, как запрос доходит до edge (DNS/anycast-маршрутизация, собственный CDN-урок edge); здесь мы остаёмся на уровне композиции — что CDN-уровень делает в твоей архитектуре и как ты им управляешь.
Push против pull CDN
Контент попадает на edge двумя способами.
Pull CDN ленив и движим спросом: edge не держит ничего, пока пользователь не запросит. Первый запрос объекта — промах: edge тянет его с origin, кэширует, и последующие запросы — попадания, пока не истечёт TTL. Это дефолт и правильный выбор для большинства сайтов: ничего не нужно заранее загружать, кэш естественно держит лишь то, что реально запрашивают, а редкий контент просто не хранится. Цена в том, что первый пользователь на объект на edge платит задержку промаха.
Push CDN жаден: ты загружаешь контент в CDN заранее (или при публикации), и он распределяется по edge до того, как кто-то спросит. Это подходит крупным предсказуемым ассетам — релиз ПО, видеотека, большой запуск, который, ты знаешь, будут долбить — где не хочется, чтобы первый пользователь съел промах на многогигабайтном файле, и хочется контролировать, когда именно контент распространяется. Цена — ты управляешь тем, что на edge, и хранишь всё, запрошено оно или нет.
Правило большого пальца: pull для общего веб-трафика (ленивый, самоуправляемый), push для крупных предсказуемых файлов, где хочешь прогреть edge заранее.
Cache-Control, TTL и hit ratio, который за всё платит
CDN не угадывает, сколько кэшировать — это ты ему говоришь HTTP-заголовками кэша. Cache-Control: max-age=3600 говорит «это свежо 3600 секунд»; после edge ревалидирует (спрашивает origin «ещё годно?» через ETag/If-None-Match, получая дешёвый 304 Not Modified, если да). s-maxage целит именно в разделяемые кэши (CDN); no-store запрещает кэширование целиком; private разрешает браузеру, но не CDN. TTL — самая значимая ручка: слишком короткий — hit ratio рушится (всё ревалидирует, origin долбят, ты теряешь разгрузку); слишком длинный — отдаёшь устаревший контент, который не забрать легко (хук). Прежде чем выставить TTL, спроси себя: какова цена ошибки в каждую сторону — устаревшие данные у клиентов или перегруженный origin?
Число, решающее, стоит ли CDN своих денег, — cache hit ratio (доля попаданий) — попадания ÷ всего запросов. Экономика нелинейна: переход с 90% на 95% hit ratio вдвое срезает трафик origin (10% → 5% запросов доходят до origin), а 95% → 99% снова делит вдвое. Поскольку compute и egress origin — дорогая часть, малые приросты hit ratio оборачиваются крупным выигрышем в стоимости и ёмкости. Ты поднимаешь его разумными TTL, нормализацией ключей кэша (не давай фрагменту трекинг-строки query разбить кэш на тысячу почти одинаковых копий) и origin shielding.
Origin shielding и давка
Сотни edge-узлов означают сотни независимых кэшей — и на первом запросе популярного объекта (или сразу после истечения его TTL) каждый edge может промахнуться разом и устроить давку origin одним и тем же запросом: давка (thundering herd) масштаба CDN (та же форма отказа, что failover балансировщика, теперь на уровне кэша). Origin shielding чинит это, назначая один (или несколько) промежуточных PoP, через которые воронкой идут все промахи edge; shield тоже кэширует, поэтому origin видит один запрос на объект вместо одного-на-edge. В паре с объединением запросов (request coalescing) (edge схлопывает много одновременных промахов одного ключа в единый запрос к апстриму) origin защищён даже при всплеске на холодном кэше.
▸Почему это работает
Почему инвалидация — по-настоящему трудная часть любого кэша, включая CDN? Потому что edge распределён и далеко: устаревшая копия может лежать на сотнях узлов по планете, и дотянуться до каждого дёшево нельзя. Есть две стратегии. Истечение (TTL) пассивно — поставь достаточно короткий TTL, и устаревание само залечится, но ты меняешь hit ratio на свежесть и всё равно отдаёшь устаревшее до одного TTL. Purge активен — явно велишь CDN выселить объект (по URL) или группу (по cache-тегу/surrogate key), что распространяется на все edge за секунды. Старший приём, обходящий всю проблему, — cache-busting через URL с хешем содержимого: называй неизменяемые ассеты app.4f9a1c.js и отдавай с годовым TTL, затем меняй сам URL (хеш) при каждом деплое. Старый URL больше не запрашивается, а новый — гарантированный промах-затем-попадание; ты ничего не инвалидируешь, потому что имя само кодирует версию.
Динамический контент, edge-вычисления и что НЕ кэшировать
Статичные ассеты (картинки, CSS, JS, видео, шрифты) — хлеб CDN. Но CDN всё чаще ведут и динамический контент: кэшируют ответы API на короткие TTL, отдают закэшированный HTML с edge-вычислениями (Workers/Lambda@Edge), персонализирующими на периметре, или кэшируют статичную оболочку и тянут лишь динамические куски. Линия движется, но дисциплина неизменна: кэшируй агрессивно то, что одинаково для всех и безопасно при лёгком устаревании, и никогда не кэшируй то, что на пользователя или обязано быть верным сейчас.
▸Частая ошибка
Опасная ошибка — кэшировать на пользователя или чувствительные ответы в разделяемом кэше CDN. Закэшируй аутентифицированную страницу или ответ API, меняющийся по залогиненному пользователю, — и edge может отдать баланс счёта Пользователя A, его корзину или сессию Пользователю B, запросившему тот же URL — реальный и повторяющийся класс инцидентов с утечкой данных. Правила: всё персонализированное или приватное обязано быть Cache-Control: private (только браузер) или no-store; всё, что варьируется по заголовку (язык, аутентификация), обязано объявить это через Vary, чтобы кэш ключевался по нему; и никогда не кэшируй ответы с Set-Cookie в разделяемом кэше. Вне игры также: всё, что обязано быть мгновенно верным (живые остатки, цены, чьё устаревание нетерпимо — хук), и ответы на запись/POST. Сомневаешься насчёт ответа на пользователя — безопасный дефолт «не кэшировать на разделяемом edge»: выигрыш задержки никогда не стоит утечки данных одного пользователя другому.
Твой hit ratio CDN — 90%; оптимизация поднимает его до 95%. Origin обрабатывал 10 000 RPS промахов. Что примерно происходит с нагрузкой origin и почему малое изменение доли так важно?
Страница цен отдаётся с Cache-Control: max-age=86400 из pull CDN. Ты пушишь изменение цены, и клиенты по миру до суток видят старую цену. Что произошло и каков надёжный фикс?
Чтобы сотни edge-узлов не промахнулись разом и не устроили давку origin одним и тем же запросом, CDN воронкой пускает промахи edge через один региональный промежуточный узел, который тоже кэширует — приём зовётся origin _______ — так что origin видит один запрос на объект вместо одного на edge.
- 01Какие две выгоды даёт CDN и как hit ratio их движет?
- 02Push против pull CDN — как каждый доставляет контент на edge и когда какой брать?
- 03Почему инвалидация — трудная часть и какой приём её обходит?
- 04Что НЕ класть в разделяемый кэш CDN и как защищаться от утечек?
CDN — глобальный парк edge-узлов (PoP — points of presence, точки присутствия), кэширующих контент рядом с пользователями, что покупает две складывающиеся выгоды: меньшую задержку (близкий edge вместо далёкого origin) и разгрузку origin (каждое попадание — запрос, который origin не видит, поэтому 95% попаданий ≈ в 20× меньший origin). Контент попадает на edge двумя путями — pull (ленивый, движимый спросом, дефолт для веб-трафика) и push (жадный прогрев заранее, для крупных предсказуемых ассетов). Свежестью управляешь через Cache-Control/TTL и ревалидацию, а метрика, что за всё платит, — cache hit ratio (доля попаданий в кэш), чья экономика нелинейна (90%→95% делит трафик origin вдвое). Origin shielding плюс объединение запросов гасят давку масштаба CDN, когда популярные объекты холодеют. По-настоящему трудная часть — инвалидация: purge активен, TTL пассивен, а приём, обходящий проблему, — URL с хешем содержимого и длинные TTL. Наконец, знай что не кэшировать на разделяемом edge: на пользователя или чувствительные ответы (риск утечки — private/no-store/Vary), обязанные быть верными данные и записи. Инцидент с устаревшей ценой из хука — весь урок: положить контент на edge легко; забрать обратно — проектное решение, которое принимаешь намеренно. Теперь, когда после деплоя придут жалобы на устаревшие данные, первые вопросы будут: какой TTL стоял, был ли запущен purge и завязаны ли затронутые URL на хеш содержимого?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.