Кэш почти неизбежен, когда вы создаете API для системы, которая требует высокой пропускной способности с минимальным временем отклика. Каждый разработчик, работающий над распределенными системами, в какой-то момент использовал какой-либо механизм кэширования. В этой статье мы рассмотрим дизайн, в котором вы используете CDN для создания сквозного кеша, который не только оптимизирует ваши API, но и снижает стоимость вашей инфраструктуры.

Для понимания этой статьи было бы полезно иметь некоторое представление о кеше и CDN. Если вы совершенно не осведомлены, я прошу вас прочитать немного, а затем вернуться сюда.

Немного предыстории

На момент написания этой статьи я работал с Glance, крупнейшей в мире платформой для обнаружения контента на основе экранов блокировки, и на данный момент у нас около 140 миллионов активных пользователей. Итак, вы можете представить себе масштаб, в котором должны работать наши сервисы, и в этом масштабе даже самая простая вещь становится сверхсложной. И как бэкэнд-разработчики мы всегда стремимся создавать высокооптимизированные API-интерфейсы, чтобы обеспечить удобство для наших пользователей. История начинается с того, как мы столкнулись с определенной проблемой, а затем приступили к ее решению. Я надеюсь, что после прочтения этой статьи вы сможете кое-что узнать о системном дизайне в широком масштабе.

Проблема

Нам пришлось разработать пару API, которые имели следующие характеристики

  1. Данные не собирались меняться очень часто.
  2. Ответ одинаков для всех пользователей, без неожиданных параметров запроса, простых прямых GET API.
  3. Объем данных ответа составил ~ 600 Кбайт на макс.
  4. Мы ожидали очень высокой пропускной способности (в конечном итоге около 50–60 тыс. Запросов в секунду) от API.

Итак, что приходит вам в голову, когда вы впервые видите эту проблему? Что ж, для меня это было так, просто добавьте кеш в памяти (может быть google guava) на узлах (поскольку объем данных низкий), используйте Kafka для отправки сообщений об аннулировании (потому что я люблю Kafka 😆 и это надежно), установите автоматическое масштабирование для экземпляров службы (поскольку трафик не был равномерным в течение дня). Что-то вроде этой диаграммы

Бац! Задача решена! легко, правда? Ну, не совсем, как и любой другой дизайн, у этого тоже были некоторые недостатки. Например, дизайн немного сложен для простого случая использования, стоимость инфраструктуры будет расти, так как теперь нам нужно создать кластер Kafka + Zookeeper, плюс для обработки 50 тыс. Запросов в секунду нам нужно горизонтально масштабировать наш сервис. экземпляров (подов Kubernetes для нас) много, что означает увеличение количества голых металлических узлов или виртуальных машин.

Следовательно, мы искали более простой и экономичный подход, и именно так мы закончили разработку решения с «Чтением через кеш с использованием CDN». Я расскажу о деталях архитектуры, а также о компромиссах через некоторое время.

Но прежде чем идти дальше, давайте разберемся со строительными блоками дизайна.

Читать через кеш

Стандартные стратегии обновления кеша таковы

  1. Кэш отдельно
  2. Полное чтение
  3. Сквозная запись
  4. Ответить
  5. Обновить вперед

Я не буду вдаваться в подробности других стратегий и сосредоточусь только на прочтении, поскольку эта статья посвящена только этому. Давайте копнем немного дальше и разберемся, как это работает.

user1 - ›просто воображаемый фрагмент данных, который вы пытаетесь получить

Схема не требует пояснений, но просто резюмирует

  1. Приложение никогда не взаимодействует с БД напрямую, а всегда через кеш.
  2. При промахе кеша кеш будет читать из БД и пополнять хранилище кеша.
  3. При попадании в кеш данные обслуживаются из кеша.

Вы можете видеть, что доступ к БД осуществляется очень редко, а ответ быстрый, поскольку кеши в основном находятся в памяти (Redis / Memcached). Уже решено много проблем 😅

CDN

Определение CDN в Интернете: «Сеть доставки контента (CDN) - это глобально распределенная сеть прокси-серверов, обслуживающих контент из мест, расположенных ближе к пользователю, и используется для обслуживания статических файлов, таких как изображения, видео и т. Д. HTML, CSS файлы ». Но мы собираемся пойти против потока и обслуживать динамический контент (ответ JSON, а не файл JSON) с помощью CDN.

Кроме того, концептуально существует два типа сетей CDN.

  1. Push CDN: вы несете ответственность за загрузку данных на серверы CDN.
  2. Получение CDN: CDN будет получать данные с ваших серверов (исходных серверов).

Мы собираемся использовать Pull CDN, поскольку с подходом push я должен позаботиться о повторных попытках, идемпотентности и других вещах, что является для меня дополнительной головной болью и на самом деле не добавляет никакой ценности для этого варианта использования.

CDN как кэш для чтения

Идея проста: мы помещаем CDN в качестве уровня кэширования между пользователями и собственно серверными службами.

Как видите, CDN находится между клиентом и нашей серверной службой и, таким образом, становится кешем. В потоке данных последовательность выглядит так

Давайте углубимся в это, так как это суть дизайна.

Используемые сокращения

T1 - ›Временной интервал 1 + несколько миллисекунд.

T2 - ›Временной интервал 1 + 1 минута + несколько миллисекунд

TTL - ›Пора жить

Исходный сервер - ›в данном случае ваша фактическая внутренняя служба.

  1. T1: клиент вызывает пользователя user1.
  2. T1: запрос попадает в CDN.
  3. T1: Сеть CDN видит, что в ее кеш-хранилище нет ключа, связанного с user1.
  4. T1: CDN обращается к восходящему потоку, который является вашей реальной серверной службой, для получения user1.
  5. T1: серверная служба возвращает user1 в качестве стандартного ответа JSON.
  6. T1: CDN получает JSON, и теперь ему нужно его сохранить.
  7. Итак, теперь нужно решить, каким должен быть TTL для этих данных, как он это делает?
  8. Обычно есть два способа сделать это: либо исходный сервер указывает, как долго данные должны быть кэшированы, либо в конфигурации CDN установлено постоянное значение, которое использует это время для установки TTL.
  9. Лучше передать управление исходному серверу для установки TTL, чтобы у нас была возможность контролировать TTL так, как нам нравится, или иметь условный TTL.
  10. Теперь возникает вопрос, как исходный сервер определяет TTL. На помощь приходят заголовки Cache-Control. Ответ от исходного сервера может содержать заголовки управления кешем, например cache-control: public, max-age:180. Это означает, что эти данные могут быть публично кэшированы и действительны в течение 180 секунд.
  11. T1: Теперь CDN видит это и кэширует данные с TTL 180 секунд.
  12. T1: CDN отвечает вызывающему абонентом JSON пользователя 1.
  13. T2: Другой клиент запрашивает пользователя user1.
  14. T2: запрос попадает в CDN.
  15. T2: CDN видит, что в его хранилище присутствует ключ user1, поэтому он не обращается к исходному серверу и не возвращает кэшированный ответ JSON.
  16. T3: Срок действия кеша на CDN истекает через 180 секунд.
  17. T4: Некоторые клиентские запросы для пользователя user1, но поскольку кеш пуст, поток снова начинается с шага 3. И это продолжает повторяться.

Необязательно, чтобы TTL составлял всего 180 секунд. Выберите TTL в зависимости от того, как долго вы можете обслуживать устаревшие данные, и не беспокойтесь об этом. И если возникает вопрос, почему нельзя аннулировать кеш при изменении данных, подождите, я отвечу на него в ближайшее время в разделе о недостатках.

Реализация

До сих пор я в основном говорил о дизайне и не вдавался в фактическую реализацию. Причина в том, что дизайн довольно прост для реализации в любой установке. Для нас наш CDN находился в облаке Google, а кластер Kubernetes, в котором работают серверные службы, - в Azure, поэтому мы выполнили настройку в соответствии с нашими потребностями. Вы можете сделать это, например, на Cloudflare CDN, поэтому не вдавайтесь в реализацию и сохраните ее абстрактной. Но просто для любопытных, вот как выглядит наша производственная установка.

Ничего страшного, если вы этого не понимаете, если вы понимаете концепции, построить это будет проще простого.

Вот замечательный документ от сотрудников Google Cloud, который поможет вам начать работу.

Свернуть запрос

[Добавление после того, как Абхишек Сингла спросил об этом в комментариях]

Однако есть еще одна проблема: CDN берет на себя всю нагрузку за нас, и нам не нужно расширяться. Но мы работаем со скоростью 60 тыс. Запросов в секунду, что означает, что в случае промаха кеша 60 тыс. Вызовов попадут в нашу исходную службу (учитывая, что для заполнения кеша CDN требуется 1 секунда), и это может привести к перегрузке службы, верно?

Вот тут-то на сцену выходит сворачивание запроса.

Как следует из названия, он в основном объединяет несколько запросов с одинаковыми параметрами запроса и отправляет очень небольшое количество запросов на исходный сервер.

Прелесть нашего дизайна в том, что нам не нужно самостоятельно выполнять сворачивание запросов, и CDN поможет нам в этом. Как я уже упоминал, мы используем Google Cloud CDN, в котором есть концепция объединения запросов, которая является просто еще одним названием для свертывания запросов. Поэтому, когда одновременно выполняется много запросов на заполнение кеша, CDN в основном идентифицирует это и отправляет только один запрос на узел из CDN на исходный сервер, а затем отвечает на все эти запросы из этого ответа. Именно так он защищает наш исходный сервер от высокого трафика.

Хорошо, сейчас мы приближаемся к концу, и любой дизайн не завершен с анализом плюсов и минусов. Итак, давайте немного проанализируем дизайн и посмотрим, как он помогает, а как нет.

Плюсы дизайна

  1. Простота. Этот дизайн очень прост, его легко реализовать и поддерживать.
  2. Время ответа: вы уже знаете, что серверы CDN географически расположены для оптимизации передачи данных, и из-за этого время нашего ответа также стало очень быстрым. Например, как звучит 60 мс (без учета времени установления TCP-соединения)?
  3. Сниженная нагрузка: поскольку фактические серверные серверы теперь получают ~ 1 запрос / 180 секунд, нагрузка очень мала.
  4. Стоимость инфраструктуры. Если бы мы этого не сделали, чтобы справиться с такой нагрузкой, нам пришлось бы сильно масштабировать нашу инфраструктуру, что связано со значительными затратами. Но в Glance мы уже вложили значительные средства в CDN, поскольку мы являемся платформой для контента, так почему бы не использовать это. Увеличение стоимости поддержки этих API-интерфейсов сейчас незначительно.

Обратная сторона дизайна

  1. Недействительность кеша. Аннулирование кеша - одна из самых сложных задач в компьютерных науках, а с учетом того, что CDN становится кешем, это становится еще сложнее. Любая импровизированная аннулирование кеша в CDN - дорогостоящий процесс и, как правило, не происходит в реальном времени. В случае изменения ваших данных, поскольку мы не можем сделать кеш в CDN недействительным, ваши клиенты могут получить устаревшие данные в течение некоторого времени. Но это снова зависит от установленного вами TTL, если ваш TTL находится в часах, вы также можете вызвать недействительность кеша на CDN. Но если TTL будет в секундах / минутах, это будет проблематично. Также имейте в виду, что не все поставщики CDN предоставляют API для аннулирования кеша CDN.
  2. Меньше контроля. Поскольку запросы сейчас не поступают на наши серверы, может возникнуть ощущение, что как разработчик вы не обладаете достаточным контролем над системой (или, может быть, я просто помешан на контроле 😈). И наблюдаемость может немного пострадать, вы всегда можете настроить ведение журнала и мониторинг в CDN, но обычно это связано с расходами.

Несколько мудрых слов

Любой дизайн в распределенном мире немного субъективен и всегда требует компромиссов. Наш долг как разработчиков / архитекторов - взвесить компромисс и выбрать тот дизайн, который подходит нам. Сказав, что ни один дизайн не является достаточно конкретным, чтобы продолжаться вечно, поэтому, учитывая ограничения, мы выбрали определенный дизайн, и в зависимости от того, как он работает для нас, мы можем развивать его и дальше.

Спасибо за прочтение!

Я Аритра Дас, я разработчик, и мне очень нравится создавать сложные распределенные системы. Не стесняйтесь обращаться ко мне в Linkedin или Twitter по любым вопросам, связанным с технологиями.

Удачного обучения…