Никогда не используйте базу данных в качестве очереди сообщений. Сделайте это вместо этого.

Наивные архитектуры пропускают реализацию очередей сообщений, но позже это вызывает проблемы. Прочитайте это, чтобы узнать, почему это плохо.

  • Современные серверные части, ориентированные на работу в реальном времени, зависят от очереди сообщений на стороне сервера, чтобы своевременно предоставлять пользователям услуги.
  • Разработчики быстро меняющихся приложений пропускают очереди обмена сообщениями, потому что их сложно понять и создать, но это ошибка.
  • Заимствуя такие понятия, как ORM в мире баз данных, более высокая степень абстракции может помочь решить распространенные архитектурные проблемы, уменьшив при этом сложность.

Если вы разработчик приложений и вам нужен полный бэкенд, включая уровень данных, у вас есть два варианта:

  • Положитесь на бэкэнд-команду.
  • Создайте свой собственный с помощью инструментов, которые приближают уровень абстракции к вам.

Доверие к бэкэнд-команде может быть решением, которое мы все хотим. Но часто, особенно с зарождающимися проектами, мы не можем себе этого позволить.

Разработчики хотят быстро продвигать что-то готовое к производству и должны работать на высоком уровне абстракции, поэтому у нас есть такие вещи, как Firebase, или такие инструменты, как ORM, для упрощения сохраняемости данных.

В этом посте мы покажем, как отсутствие хороших абстракций приводит к созданию собственных бэкендов, в которых отсутствует ключевой компонент, присутствующий в большинстве современных бэкендов: очередь сообщений.

Но что такое серверная часть?

Если вы спросите большинство инженеров, серверная часть состоит из базы данных, уровня бизнес-логики для обеспечения доступа к этим данным и аутентификации пользователя.

Но если вы спросите современных бэкенд-инженеров, ответ будет другим. Да, у вас есть все это, но на этой картинке отсутствует важный компонент: очередь сообщений.

От новых опций, таких как Redpanda и Upstash, до устоявшихся систем, таких как Kafka, очереди сообщений являются неотъемлемой частью большинства используемых сегодня профессиональных бэкэндов.

Зачем очереди сообщений?

Пользователи все чаще ожидают взаимодействия в реальном времени. Для таких вариантов использования, как потоки заказов, веб-перехватчики и отслеживание пользователей, пользователи ожидают мгновенного просмотра новых данных в пользовательском интерфейсе, вместо того, чтобы ждать периодического обновления какой-либо фоновой пакетной обработки.

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

Давайте рассмотрим, например, проблему обработки веб-хуков: ваш серверный сервис получает поток HTTP-событий. Их приходится обрабатывать обычно (но не всегда) по порядку. Поступившие события не могут быть потеряны и должны быть обработаны ровно один раз. Обработка события обычно означает:

  • Преобразование его во внутренний формат (обычно на основе json), который имеет смысл для вашего приложения.
  • Обработка события (с побочными эффектами), которая может включать вызов других веб-служб или API.
  • Сохранение конечного результата этого события в базе данных.

Системы потоковой обработки и очереди сообщений — правильное решение для создания отзывчивых приложений, которые пользователи воспринимают как работу в режиме реального времени. Однако, если вы разработчик приложений, готовящий потрясающий прототип для своей новой классной идеи, вы, скорее всего, вместо этого используете базу данных.

Разве я не могу использовать базу данных в качестве очереди сообщений?

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

Если вы никогда раньше не сталкивались с этой проблемой, наивная реализация на основе базы данных будет выглядеть так, как показано ниже:

Это может сработать для начального тестирования, но теперь нам нужно разобраться с режимами отказа и семантикой доставки:

  • Если нам не удастся преобразовать данные, не обработать или не сохранить, событие может быть потеряно.
  • Политики повторных попыток могут повысить надежность. Но что, если код просто рухнет?

Конечно, вы можете продолжать накапливать исправления для этих проблем, но в конечном итоге приходит время перейти к более серьезной архитектуре:

Теперь ваш исходный код обработки веб-перехватчика ничего не делает, кроме как сохраняет событие. Простой, атомарный, беззаботный.

Позже, с помощью некоторого фонового пакетного процесса, вы можете прочитать ожидающие события, преобразовать, обработать и, когда вы будете уверены, что они были обработаны, удалить событие из очереди.

Это работает достаточно хорошо и ведет вас дальше. Но также имеет проблемы:

  • Теперь вы опрашиваете очередь, и установить правильный период опроса проблематично.
  • Вы можете сделать это лучше с помощью триггеров базы данных, но по мере того, как ваш список ожидающих событий растет, их выбор увеличивает нагрузку на базу данных.
  • Вы можете добавить больше проиндексированных полей, чтобы ускорить опрос, но… что ж, теперь вы снова нуждаетесь в специальных знаниях, чтобы работать с этим хорошо.

Но когда мы смотрим на адаптированные серверные части в открытом доступе, особенно в архитектурах для FAANG, MAMAA или какой-либо другой аббревиатуры, пока Цук в следующий раз не решит изменить Facebook, все выглядит иначе. Чаще всего у вас будет очередь сообщений, обрабатывающая эти события:

Если очереди сообщений имеют такой смысл, почему их не включили в первую реализацию серверной части приложения?

Потому что шаблоны баз данных хорошо изучены. Продукты размещенных баз данных есть везде. Преобладают ORM, которые абстрагируют базу данных и делают все возможное, чтобы скрыть детали реализации. Совсем другая ситуация с очередями сообщений. Я не могу придумать ни одной узнаваемой ORM-подобной библиотеки с торговой маркой, которая будет абстрагировать Kafka таким образом, который будет иметь смысл для разработчиков приложений.

ChiselStrike повышает уровень абстракции

ChiselStrike обеспечивает уровень абстракции высокого уровня для всего бэкенда, который может привести вас от прототипа к производству. Вы можете сосредоточиться на кодировании того, что вам нужно, на уровне абстракции, который вы понимаете, и он будет обрабатывать сопоставление с внутренними компонентами.

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

С нашим новым выпуском 0.12 мы, наконец, сделали важный шаг на пути к нашему видению предоставления возможностей современных надежных серверных приложений разработчикам приложений: теперь вы можете подключить Kafka-совместимый потоковый сервис, такой как Redpanda и Upstash, к ChiselStrike и написать TypeScript для реализации любую логику, которую вам нужно запустить в этой очереди. Это по-прежнему предполагает наличие очереди сообщений, но уже открывает важные варианты использования, когда ваш бэкенд теперь является частью более широкой архитектуры, включающей очередь сообщений. В будущих выпусках мы также внедрим очередь сообщений так же, как сегодня мы внедряем базу данных, что позволит вам вообще не беспокоиться о ее развертывании.

Куда пойти отсюда?

ChiselStrike устраняет пробел в абстракции, добавляя очереди сообщений и даже позволяя вам взаимозаменяемо использовать одни и те же типы между кодом базы данных и кодом событий.

Если вы хотите попробовать пример из этого поста самостоятельно, вот полная установка, включая инструкции по публикации в местной теме Kafka.