Logo Craft Homelab Docs Контакты Telegram
NATS: лёгкий мессенджер — Pub/sub, jetstream Трендовые github проекты в нашем телеграм канале. Подпишись 👉
Wed Feb 11 2026

NATS: лёгкий мессенджер

NATS — это мессенджер, который ломает шаблоны традиционных брокеров. Когда Kafka с его zookeeper и partitioning становится тяжелым, а RabbitMQ — сложным для микросервисов, NATS предлагает радикально простой подход: минимальные зависимости, наносекундная задержка и встроенный pub/sub без лишних конфигураций. Но за этой простотой скрываются важные компромиссы, которые мы разберем в этой статье.

Архитектура NATS: простота с подвохом

NATS построен на принципе “публикуй и подписывай” без центрального маршрутизатора. Вместо сложных эксчейнджей и очередей, как в RabbitMQ, NATS использует плоскую модель Subjects (тем), где любой клиент может публиковать сообщения, а другие — подписываться.

Сервер NATS (gnatsd) выполняет лишь одну задачу: принимает сообщения и пересылает их всем подписчикам соответствующей темы. Это делает его чрезвычайно быстрым — задержки измеряются микросекундами. Но есть и обратная сторона: по умолчанию сообщения не сохраняются. Если подписчик не онлайн, он пропустит сообщение.

Вот здесь на сцену выходит JetStream — расширение NATS для персистентности. Он добавляет:

  • Хранение сообщений
  • Повторную доставку
  • Управление потоками и консьюмерами
  • Квантификацию сообщений (каждое получает последовательный номер)

JetStream работает на концепции Streams (потоков) и Consumers (консьюмеров). Stream — это упорядоченное хранилище сообщений, а Consumer — точка доступа с возможностью управления тем, какие сообщения и в каком порядке доставляются.

Практическая интеграция с Python

Для работы с NATS в Python используем библиотеку nats-py. Установка:

pip install nats-py

Пример базовой публикации и подписки:

import asyncio
import nats

async def run_basic_example():
    # Подключение к серверу NATS
    nc = await nats.connect("nats://localhost:4222")
    
    # Публикация сообщения
    await nc.publish("updates.system", b"System is running")
    
    # Простая подписка
    async def handler(msg):
        print(f"Получено сообщение: {msg.data}")
    
    await nc.subscribe("updates.system", cb=handler)
    
    # Даем время на получение сообщений
    await asyncio.sleep(1)
    await nc.close()

asyncio.run(run_basic_example())

А теперь более сложный пример с JetStream:

import asyncio
import nats
from nats.errors import TimeoutError

async def run_jetstream_example():
    nc = await nats.connect("nats://localhost:4222")
    js = nc.jetstream()
    
    # Создание потока, если его нет
    try:
        await js.add_stream(
            name="SYSTEM_LOGS",
            subjects=["logs.>"],  # Шаблон: logs.*
            retention="limits",   # Ограничение по количеству сообщений
        )
    except nats.errors.StreamNameAlreadyInUse:
        pass  # Поток уже существует
    
    # Публикация сообщения
    await js.publish("logs.server", b"Error: Connection timeout")
    
    # Pull-подписка (JetStream)
    psub = await js.pull_subscribe("logs.server", "LOGS_CONSUMER")
    
    try:
        # Получаем одно сообщение с таймаутом
        msgs = await psub.fetch(1, timeout=1.0)
        for msg in msgs:
            print(f"Получено лог: {msg.data}")
            await msg.ack()  # Подтверждаем получение
    except TimeoutError:
        print("Нет новых сообщений")
    
    await nc.close()

asyncio.run(run_jetstream_example())

В этом примере важно отметить:

  1. Мы используем pull-подписку, которая позволяет явно запрашивать сообщения.
  2. Сообщения нужно явно подтверждать (msg.ack()), иначе они будут доставлены заново.
  3. Шаблоны Subjects (logs.>) позволяют подписаться на несколько тем одновременно.

Узкие места, которые нужно учесть в продакшене

  1. Масштабирование кластера Хотя NATS отлично справляется с нагрузкой в одном экземпляре, кластеризация требует внимательности. Каждый узел NATS должен синхронизировать состояние с другими, что при большом количестве узлов создает сетевую нагрузку. В продакшене стоит рассмотреть режимы работы full или delta синхронизации в зависимости от требований к согласованности данных.

  2. Отсутствие сложной маршрутизации В отличие от RabbitMQ с его эксчейнджами и очередями, NATS предлагает только плоскую модель Subjects. Если вашему приложению требуется сложная логика маршрутизации (например, маршрутизация на основе содержимого сообщений), придется реализовывать это на уровне приложений, что добавляет сложности.

  3. Управление состоянием JetStream При большом количестве потоков и консьюмеров управление состоянием может стать узким местом. Stream-ы в JetStream имеют ограничения по размеру и количеству сообщений, и их нужно тщательно настраивать. Неучтенное ограничение может привести к блокировке публикации новых сообщений.

  4. Проблемы с порядком сообщений Хотя внутри одного Stream-а сообщения сохраняют порядок, при использовании нескольких консьюмеров или в распределенной системе порядок может нарушиться. Если строгий порядок сообщений критичен для вашего приложения, это нужно учитывать в архитектуре.

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

Когда выбирать NATS, а когда — альтернативы

NATS идеален для сценариев, где критичны:

  • Низкая задержка и высокая пропускная способность
  • Простота развертывания и управления
  • Минимальные требования к ресурсам
  • Прямая модель pub/sub без сложной логики маршрутизации

Это делает NATS отличным выбором для:

  • Микросервисной архитектуры с высокой связанностью
  • Систем реального времени (IoT, телеметрия)
  • Веб-сокетных серверов
  • Систем, где важна скорость реакции

Однако, если вашему приложению требуются:

  • Сложная маршрутизация с условиями и фильтрацией
  • Транзакционная обработка сообщений
  • Строгий порядок сообщений в распределенной системе
  • Богатая экосистема инструментов мониторинга

Тогда стоит рассмотреть альтернативы:

  • Kafka — для потоковой обработки больших данных
  • RabbitMQ — для сложной маршрутизации и гарантированной доставки
  • Pulsar — для геораспределенных систем

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