PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    Kafka и Node.js: Event-Driven архитектура на практике

    Kafka и Node.js: Event-Driven архитектура на практике

    Аватар автора Kafka и Node.js: Event-Driven архитектура на практике

    Антон Ларичев

    Иконка календаря13 июля 2026
    kafkanodejsevent-drivenмикросервисыархитектураseniorИконка уровня senior
    Картинка поста Kafka и Node.js: Event-Driven архитектура на практике

    Введение

    Event-Driven архитектура (EDA) позволяет компонентам системы взаимодействовать через события, не зная ничего друг о друге. Apache Kafka — один из наиболее популярных инструментов для реализации такого подхода в production-среде. В этой статье разберём, как интегрировать Kafka с Node.js: от настройки продюсера и консьюмера до обработки ошибок и управления offset-ами.

    Что такое Event-Driven архитектура

    В традиционной архитектуре сервисы общаются напрямую — один вызывает другой синхронно. EDA меняет этот подход: компоненты публикуют события, а другие компоненты подписываются на них. Это даёт три ключевых преимущества:

    • Слабая связанность: сервисы не зависят друг от друга напрямую
    • Масштабируемость: потребителей можно горизонтально масштабировать независимо
    • Отказоустойчивость: сбой одного сервиса не блокирует остальные

    Apache Kafka реализует эту модель через концепцию топиков (topics), партиций (partitions) и групп потребителей (consumer groups).

    Установка и настройка

    Начнём с локального запуска Kafka через Docker Compose:

    version: '3.8'
    services:
      zookeeper:
        image: confluentinc/cp-zookeeper:7.4.0
        environment:
          ZOOKEEPER_CLIENT_PORT: 2181
    
      kafka:
        image: confluentinc/cp-kafka:7.4.0
        depends_on:
          - zookeeper
        ports:
          - "9092:9092"
        environment:
          KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
          KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
          KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
    

    Установим kafkajs — наиболее зрелый Kafka-клиент для Node.js:

    npm install kafkajs
    

    Создание продюсера

    Продюсер публикует сообщения в топик. Создадим типизированный сервис:

    import { Kafka, Producer } from 'kafkajs';
    
    const kafka = new Kafka({
      clientId: 'order-service',
      brokers: ['localhost:9092'],
    });
    
    const producer: Producer = kafka.producer();
    
    async function publishOrderEvent(orderId: string, status: string): Promise<void> {
      await producer.send({
        topic: 'order-events',
        messages: [
          {
            // ключ гарантирует порядок сообщений в рамках одной партиции
            key: orderId,
            value: JSON.stringify({
              orderId,
              status,
              timestamp: new Date().toISOString(),
            }),
          },
        ],
      });
    }
    
    await producer.connect();
    await publishOrderEvent('order-123', 'CREATED');
    await producer.disconnect();
    

    Важный момент: key в сообщении определяет, в какую партицию попадёт запись. Все сообщения с одинаковым ключом гарантированно окажутся в одной партиции — это обеспечивает порядок обработки для конкретного объекта.

    Создание консьюмера

    Consumer Group позволяет нескольким экземплярам сервиса параллельно обрабатывать разные партиции:

    import { Kafka, Consumer, EachMessagePayload } from 'kafkajs';
    
    const consumer: Consumer = kafka.consumer({
      // все экземпляры с одинаковым groupId образуют одну группу
      groupId: 'notification-group',
    });
    
    interface OrderEvent {
      orderId: string;
      status: string;
      timestamp: string;
    }
    
    async function processMessage({ message }: EachMessagePayload): Promise<void> {
      if (!message.value) return;
    
      const event: OrderEvent = JSON.parse(message.value.toString());
      console.log(`Получено событие: orderId=${event.orderId}, status=${event.status}`);
    
      // здесь размещается бизнес-логика
      await sendNotification(event);
    }
    
    await consumer.connect();
    await consumer.subscribe({ topics: ['order-events'], fromBeginning: false });
    await consumer.run({ eachMessage: processMessage });
    

    Гарантии доставки и транзакции

    Kafka поддерживает три режима гарантии доставки:

    • at-most-once: сообщение может быть потеряно, но не дублировано
    • at-least-once: сообщение доставится минимум один раз, возможны дубли
    • exactly-once: ровно одна доставка, требует транзакций

    Для критичных операций используйте транзакционного продюсера:

    const txProducer = kafka.producer({
      transactionalId: 'order-tx-producer',
      maxInFlightRequests: 1, // обязательно для транзакций
      idempotent: true,       // предотвращает дубли при повторной отправке
    });
    
    await txProducer.connect();
    const transaction = await txProducer.transaction();
    
    try {
      await transaction.send({
        topic: 'order-events',
        messages: [{ key: 'order-456', value: JSON.stringify({ status: 'PAID' }) }],
      });
      await transaction.send({
        topic: 'payment-events',
        messages: [{ key: 'order-456', value: JSON.stringify({ amount: 1500 }) }],
      });
      // фиксируем обе записи атомарно
      await transaction.commit();
    } catch (error) {
      await transaction.abort(); // откатываем при любой ошибке
      throw error;
    }
    

    Частые ошибки

    1. Нет обработки ошибок в консьюмере

    Если eachMessage выбрасывает необработанное исключение, KafkaJS останавливает всю группу потребителей. Оборачивайте логику в try/catch и явно решайте: повторить, отправить в dead-letter queue или пропустить сообщение.

    2. Неверное число партиций

    Число партиций определяет максимальный параллелизм: при 3 партициях максимум 3 консьюмера из одной группы работают одновременно. Уменьшить число партиций после создания топика нельзя — планируйте с запасом.

    3. Игнорирование ручного commit offset

    При autoCommit: true offset фиксируется до завершения обработки. При сбое сообщение будет потеряно. Для критичных данных используйте ручной commit:

    await consumer.run({
      autoCommit: false,
      eachMessage: async (payload) => {
        await processMessage(payload);
        // фиксируем только после успешной обработки
        await consumer.commitOffsets([{
          topic: payload.topic,
          partition: payload.partition,
          offset: String(Number(payload.message.offset) + 1),
        }]);
      },
    });
    

    4. Утечки соединений

    Всегда вызывайте disconnect() при завершении процесса:

    process.on('SIGINT', async () => {
      await consumer.disconnect();
      process.exit(0);
    });
    

    Заключение

    Event-Driven архитектура с Kafka открывает возможности для построения масштабируемых и отказоустойчивых Node.js-систем. Ключевые принципы: выбирайте гарантии доставки под конкретную задачу, планируйте партиции заранее, обрабатывайте ошибки явно и управляйте offset-ами осознанно. Начните с простого продюсера и консьюмера, а по мере роста нагрузки добавляйте транзакции и мониторинг через Kafka UI или Confluent Control Center.

    Иконка глаза1

    Комментарии

    0

    Постройте личный план изучения JavaScript Advanced - продвинутые концепции языка и ООП до уровня Middle — бесплатно!

    JavaScript Advanced - продвинутые концепции языка и ООП — часть карты развития Frontend, Backend, Mobile

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    TypeScript с нуля

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    React и Redux Toolkit

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Neovim

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста GraphQL vs REST: что выбрать для API в 2026 году
    Иконка аватараАнтон
    Иконка календаря11 июля 2026
    graphqlrestapi+ 3middleИконка уровня middle

    GraphQL vs REST: что выбрать для API в 2026 году

    GraphQL vs REST — разбираем ключевые отличия, сценарии применения и подводные камни каждого подхода, чтобы выбрать правильный инструмент для вашего проекта.

    Иконка чипа0
    Иконка глаза72
    Иконка комментариев0
    Картинка поста Микрофронтенды и Module Federation: подходы и реализация
    Иконка аватараАнтон
    Иконка календаря06 июля 2026
    микрофронтендыmodule-federationwebpack+ 2seniorИконка уровня senior

    Микрофронтенды и Module Federation: подходы и реализация

    Микрофронтенды с Module Federation: как разбить монолитный фронтенд на независимые части, которые деплоятся и разрабатываются отдельно.

    Иконка чипа0
    Иконка глаза177
    Иконка комментариев0
    Картинка поста Clean Architecture: чистая архитектура для веб-разработки
    Иконка аватараАнтон
    Иконка календаря02 июля 2026
    архитектураclean architecturetypescript+ 3middleИконка уровня middle

    Clean Architecture: чистая архитектура для веб-разработки

    Clean Architecture разделяет код на независимые слои: бизнес-логика не зависит от фреймворков, БД и внешних сервисов. Разбираем на примерах TypeScript.

    Иконка чипа0
    Иконка глаза209
    Иконка комментариев0
    Иконка чипа0