Что такое NATS?

«Что такое NATS?» — вопрос из категории Брокеры сообщений, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

NATS — это высокопроизводительная, легковесная система обмена сообщениями с открытым исходным кодом, написанная на Go. В мире Node.js она популярна для построения микросервисных архитектур, IoT-платформ и облачных приложений благодаря своей простоте, скорости и минимальным задержкам.

Ключевые особенности, важные для Node.js-разработчика:

  • Простота: Библиотека клиента (nats.js) минималистична, протокол текстовый/бинарный.
  • Высокая производительность: Способна обрабатывать миллионы сообщений в секунду с минимальной задержкой.
  • Модели обмена: Поддерживает основные паттерны: публикация/подписка (Pub/Sub), запрос/ответ (Request/Reply), и очереди (Queue Groups) для балансировки нагрузки.
  • Без персистентности по умолчанию: NATS Core (ядро) — это система доставки «в момент». Для persistent messaging используется NATS JetStream (дополнительный компонент).
  • Кластеризация: Брокеры легко объединяются в кластеры для отказоустойчивости.

Базовый пример Pub/Sub с официальным клиентом nats:

// publisher.js
const { connect } = require('nats');

async function publishEvent() {
  // Подключение к локальному серверу NATS
  const nc = await connect({ servers: 'nats://localhost:4222' });

  // Публикация сообщения в топик 'user.created'
  nc.publish('user.created', JSON.stringify({
    userId: 'usr_123',
    email: 'alice@example.com',
    timestamp: new Date().toISOString()
  }));
  console.log('Событие user.created опубликовано');

  await nc.drain(); // Корректное закрытие соединения
}

publishEvent().catch(console.error);
// subscriber.js
const { connect } = require('nats');

async function subscribeToEvents() {
  const nc = await connect({ servers: 'nats://localhost:4222' });

  // Создание подписки на топик 'user.created'
  const subscription = nc.subscribe('user.created');

  console.log('Подписан на user.created. Ожидание сообщений...');

  // Асинхронная итерация по входящим сообщениям
  for await (const msg of subscription) {
    const data = JSON.parse(msg.data);
    console.log(`[${msg.subject}] Получено событие для пользователя:`, data.userId);
    // Здесь может быть логика: запись в БД, отправка email и т.д.
  }
}

subscribeToEvents().catch(console.error);

Пример Request/Reply (как RPC):

// service.js (сервис-обработчик)
const { connect } = require('nats');

async function startService() {
  const nc = await connect({ servers: 'nats://localhost:4222' });

  // Подписка на запросы в топик 'get.user'
  const sub = nc.subscribe('get.user');
  for await (const msg of sub) {
    const userId = msg.data.toString();
    console.log(`Запрос данных для пользователя: ${userId}`);
    // Имитация обработки
    const userData = { id: userId, name: 'John Doe' };
    // Отправка ответа обратно отправителю
    msg.respond(JSON.stringify(userData));
  }
}

// client.js (клиент)
async function makeRequest() {
  const nc = await connect({ servers: 'nats://localhost:4222' });
  try {
    // Отправка запроса и ожидание ответа с таймаутом
    const response = await nc.request('get.user', 'usr_999', { timeout: 3000 });
    const user = JSON.parse(response.data);
    console.log('Получен ответ:', user);
  } catch (err) {
    console.error('Запрос не удался:', err.message);
  }
  await nc.close();
}

Queue Groups для балансировки нагрузки:

// Запустим несколько экземпляров этого worker-сервиса
const { connect } = require('nats');

async function startWorker(workerId) {
  const nc = await connect({ servers: 'localhost:4222' });
  // Подписка с именем группы 'user.processors'. Сообщения в топик 'user.task'
  // будут доставлены только ОДНОМУ из подписчиков в этой группе.
  const sub = nc.subscribe('user.task', { queue: 'user.processors' });
  console.log(`Worker ${workerId} запущен и ожидает задач...`);
  for await (const msg of sub) {
    console.log(`Worker ${workerId} обрабатывает: ${msg.data}`);
    // Обработка задачи...
  }
}

startWorker(1).catch(console.error);
// Запустим второй процесс: startWorker(2)

Когда выбирать NATS для Node.js-проекта?

  • Внутренняя коммуникация микросервисов: Когда нужна максимальная скорость и простота.
  • IoT и real-time данные: Благодаря малому потреблению памяти и быстрому протоколу.
  • Событийные системы (Event-Driven): Для распространения событий между компонентами.
  • Альтернативы: Для сложных маршрутизаций и гарантированной доставки смотрите на RabbitMQ. Для потоковой обработки больших данных с персистентностью — Apache Kafka. NATS занимает нишу простых, сверхбыстрых систем обмена сообщениями.