Перейти к содержимому

AsyncAPI

AsyncAPI — спецификация для документирования асинхронных API (события, очереди, стримы). Аналог OpenAPI для Kafka, MQTT, AMQP, WebSocket. Описывает каналы (топики), операции (publish/subscribe), сообщения (форматы, схемы), серверы (брокеры). Позволяет генерировать документацию, код клиентов и серверов, визуализировать потоки событий.

Введение: Документирование

Представьте, что вы проектируете почтовое отделение. Клиенты приходят, отправляют письма, получают уведомления. В отличие от телефонного разговора (синхронный запрос-ответ), почта работает асинхронно: вы отправили письмо, оно идёт какое-то время, потом приходит ответ. Нет прямого соединения.

В мире API большинство инструментов (OpenAPI, Postman) заточены под синхронный запрос-ответ: клиент отправил запрос, сервер ответил. Но современные системы всё чаще используют асинхронные паттерны: очереди сообщений (Kafka, RabbitMQ), брокеры событий, WebSocket, MQTT для IoT.

AsyncAPI — это спецификация для документирования асинхронных API. Она описывает, какие сообщения отправляются и принимаются, через какие протоколы (Kafka, MQTT, AMQP, WebSocket), какие каналы (топики, очереди) используются, какие форматы данных передаются.

AsyncAPI — это “OpenAPI для событий”. Как OpenAPI описывает REST API, так AsyncAPI описывает событийно-ориентированные и потоковые системы. Это единый язык для команд, которые проектируют системы на основе очередей сообщений и событий.

Синхронный vs Асинхронный API

ХарактеристикаСинхронный (REST)Асинхронный (события, очереди)
Время жизни соединенияЗапрос-ответ (короткое)Долгое (подписка) или отсутствует (отправил и забыл)
ОтветНемедленныйМожет прийти через минуту, час, день
НаправлениеКлиент → СерверМожет быть любым (Pub/Sub, очередь)
ПротоколыHTTP/1.1, HTTP/2Kafka, MQTT, AMQP, WebSocket, NATS
Связь отправителя и получателяПрямаяЧерез брокер (посредника)
ПримерGET /users/123Пользователь создан → событие в Kafka

Зачем нужна документация асинхронных API

Проблемы без AsyncAPI

ПроблемаПроявление
Нет контрактаКто отправляет события? Кто их читает? В каком формате?
Трудно отлаживатьСобытие прошло через пять сервисов — кто его потерял?
Сложно проектироватьНет единого чертежа системы
Дублирование кодаКаждый сервис заново описывает одни и те же события
Разные форматыОдин сервис отправляет JSON, другой — Avro

Что даёт AsyncAPI

ПреимуществоОбъяснение
Единый контрактВсе сервисы договариваются о форматах, каналах, протоколах
Автоматическая документацияВизуализация потоков событий
Генерация кодаКлиенты и серверы на 10+ языках
Валидация сообщенийПроверка соответствия схеме
Визуализация архитектурыПонятно, кто что отправляет и получает

Основные концепции AsyncAPI

Канал (Channel)

Канал — это место, куда отправляются сообщения. В разных протоколах это называется по-разному:

ПротоколНазваниеПример
KafkaTopicuser.created, order.paid
MQTTTopicsensors/temperature
AMQP (RabbitMQ)Exchange + Routing keyorders.*
WebSocketURL + событиеws://.../chat

Сообщение (Message)

Сообщение — это данные, которые передаются. AsyncAPI описывает:

  • Формат (JSON, Avro, Protobuf, XML)
  • Схема (какие поля, какие типы)
  • Заголовки (метаданные, например, correlationId)

Операции (Operations)

ОперацияЧто значитКто
subscribeПодписка на каналКлиент получает сообщения
publishПубликация в каналКлиент отправляет сообщения

Структура AsyncAPI документа

asyncapi: 3.0.0                    # версия спецификации
id: https://example.com/user-events
info:
  title: User Events API
  version: 1.0.0
  description: События жизненного цикла пользователя

servers:                           # брокеры сообщений
  production:
    host: kafka.prod.example.com:9092
    protocol: kafka
    description: Production Kafka
  staging:
    host: kafka.staging.example.com:9092
    protocol: kafka

channels:                          # каналы (топики)
  user.created:
    description: Пользователь создан
    subscribe:                     # кто-то может подписаться
      summary: Получение событий о создании пользователей
      message:
        $ref: '#/components/messages/UserCreated'
    publish:                       # кто-то может публиковать
      summary: Отправка событий о создании пользователей
      message:
        $ref: '#/components/messages/UserCreated'

  user.updated:
    description: Пользователь обновлён
    publish:
      message:
        $ref: '#/components/messages/UserUpdated'

components:
  messages:
    UserCreated:
      summary: Событие создания пользователя
      contentType: application/json
      payload:
        type: object
        required:
          - id
          - email
          - createdAt
        properties:
          id:
            type: integer
            description: Идентификатор пользователя
          email:
            type: string
            format: email
          name:
            type: string
          createdAt:
            type: string
            format: date-time
      headers:
        type: object
        properties:
          messageId:
            type: string
            description: Уникальный ID сообщения
          correlationId:
            type: string
            description: ID для трассировки

    UserUpdated:
      summary: Событие обновления пользователя
      contentType: application/json
      payload:
        type: object
        properties:
          id:
            type: integer
          changes:
            type: object
            additionalProperties: true
          updatedAt:
            type: string
            format: date-time

Компоненты AsyncAPI (подробно)

Info (метаинформация)

info:
  title: Order Events API
  version: 2.1.0
  description: |
    События, связанные с заказами в интернет-магазине.
    
    **События:**
    - `order.created` — заказ создан
    - `order.paid` — заказ оплачен
    - `order.shipped` — заказ отправлен
    - `order.delivered` — заказ доставлен
  contact:
    name: API Team
    email: api@example.com
  license:
    name: MIT

Servers (брокеры сообщений)

servers:
  production:
    host: kafka.prod.example.com:9092
    protocol: kafka
    description: Production Kafka кластер
    security:
      - sasl: []
  staging:
    host: kafka.staging.example.com:9092
    protocol: kafka
    description: Staging Kafka кластер
  development:
    host: localhost:9092
    protocol: kafka
    description: Local Kafka для разработки

  rabbitmq:
    host: rabbitmq.example.com:5672
    protocol: amqp
    description: RabbitMQ для задач

  mqtt:
    host: mqtt.example.com:1883
    protocol: mqtt
    description: MQTT для IoT устройств

Channels (каналы)

channels:
  order.created:
    address: orders.created
    description: Событие создания нового заказа
    messages:
      created:
        $ref: '#/components/messages/OrderCreated'

  order.{status}:
    address: orders.{status}
    description: События изменения статуса заказа
    parameters:
      status:
        description: Статус заказа
        schema:
          type: string
          enum: [paid, shipped, delivered, cancelled]
    messages:
      statusChanged:
        $ref: '#/components/messages/OrderStatusChanged'

Operations (операции)

channels:
  user.created:
    description: Канал событий создания пользователей
    
    publish:                    # Сервис может отправлять
      operationId: publishUserCreated
      summary: Отправить событие о создании пользователя
      message:
        $ref: '#/components/messages/UserCreated'
    
    subscribe:                  # Сервис может получать
      operationId: subscribeUserCreated
      summary: Получить событие о создании пользователя
      message:
        $ref: '#/components/messages/UserCreated'

Messages (сообщения)

components:
  messages:
    OrderCreated:
      summary: Заказ создан
      contentType: application/json
      headers:
        type: object
        properties:
          messageId:
            type: string
            format: uuid
            description: Уникальный ID сообщения
          correlationId:
            type: string
            description: ID для трассировки
          timestamp:
            type: string
            format: date-time
            description: Время отправки
      payload:
        type: object
        required:
          - orderId
          - customerId
          - totalAmount
          - items
        properties:
          orderId:
            type: integer
            description: ID заказа
          customerId:
            type: integer
            description: ID клиента
          totalAmount:
            type: number
            format: decimal
            description: Общая сумма
          items:
            type: array
            items:
              type: object
              properties:
                productId:
                  type: integer
                quantity:
                  type: integer
                price:
                  type: number
          createdAt:
            type: string
            format: date-time
      examples:
        - name: typical
          summary: Типичный заказ
          headers:
            messageId: 123e4567-e89b-12d3-a456-426614174000
            correlationId: req-789
            timestamp: 2024-01-15T10:30:00Z
          payload:
            orderId: 1001
            customerId: 456
            totalAmount: 5000.00
            items:
              - productId: 123
                quantity: 1
                price: 5000.00
            createdAt: 2024-01-15T10:30:00Z

Schemas (схемы данных)

components:
  schemas:
    Money:
      type: object
      properties:
        amount:
          type: number
          minimum: 0
        currency:
          type: string
          pattern: '^[A-Z]{3}$'
          example: RUB
    
    Address:
      type: object
      properties:
        city:
          type: string
        street:
          type: string
        zip:
          type: string
    
    Customer:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
        address:
          $ref: '#/components/schemas/Address'

Security (безопасность)

components:
  securitySchemes:
    sasl:
      type: scramSha256
      description: SASL SCRAM-SHA-256 аутентификация
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
    oauth2:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://auth.example.com/token
          scopes:
            read:events: Чтение событий
            write:events: Отправка событий

servers:
  production:
    host: kafka.prod.example.com:9092
    protocol: kafka
    security:
      - sasl: []

Протоколы и их особенности в AsyncAPI

Kafka

servers:
  kafka-prod:
    host: kafka-cluster.prod.example.com:9092
    protocol: kafka
    protocolVersion: 3.0

channels:
  user.events:
    address: users
    description: Топик для событий пользователей
    bindings:
      kafka:
        topic: user-events
        partitions: 6
        replicas: 3

Особенности Kafka:

  • Топики могут иметь несколько партиций
  • Сообщения могут иметь ключи (key)
  • Порядок сообщений гарантирован внутри партиции

MQTT

servers:
  mqtt-prod:
    host: mqtt.example.com:1883
    protocol: mqtt
    protocolVersion: 5

channels:
  sensors:
    address: sensors/{deviceId}/temperature
    description: Поток температурных данных
    parameters:
      deviceId:
        description: ID устройства
        schema:
          type: string

Особенности MQTT:

  • Уровни QoS (0, 1, 2)
  • Last Will and Testament (LWT)
  • Retained messages

AMQP (RabbitMQ)

servers:
  rabbit-prod:
    host: rabbitmq.example.com:5672
    protocol: amqp
    protocolVersion: 0-9-1

channels:
  orders:
    address: orders.exchange
    description: Exchange для заказов
    bindings:
      amqp:
        exchange: orders
        type: topic
        durable: true

Особенности AMQP:

  • Exchange и Queue
  • Routing keys
  • Bindings

WebSocket

servers:
  ws-prod:
    host: ws.example.com
    protocol: ws
    protocolVersion: 13

channels:
  chat:
    address: /chat
    description: Чат в реальном времени

Практические сценарии использования

Сценарий 1: Событийная архитектура микросервисов

Ситуация: В системе 20 микросервисов, которые общаются через Kafka. Кто какие события публикует? Кто на какие подписан?

Действия:

  1. Создать AsyncAPI спецификацию для всех событий
  2. Описать каждый топик (канал)
  3. Указать, какой сервис публикует (publish), какой подписан (subscribe)
  4. Визуализировать потоки событий

Результат: Единая карта коммуникаций между сервисами.

Сценарий 2: IoT платформа

Ситуация: Тысячи устройств отправляют телеметрию через MQTT. Нужно стандартизировать форматы сообщений.

Действия:

  1. Описать каналы для каждого типа устройств
  2. Определить схемы сообщений (температура, влажность, GPS)
  3. Добавить примеры для разных сценариев

Результат: Разработчики прошивок и бэкенда работают по единому контракту.

Сценарий 3: Аудит и трассировка

Ситуация: Нужно понять, почему событие не дошло до получателя.

Действия:

  1. Описать обязательные заголовки (correlationId, messageId, timestamp)
  2. Задокументировать, какие сервисы должны логировать входящие/исходящие события
  3. Использовать спецификацию для генерации кода трассировки

Результат: Возможность отследить путь события через всю систему.

AsyncAPI vs OpenAPI

ХарактеристикаOpenAPIAsyncAPI
Тип взаимодействияСинхронное (запрос-ответ)Асинхронное (события, очереди)
ПротоколыHTTPKafka, MQTT, AMQP, WebSocket, NATS
НаправлениеКлиент → СерверPub/Sub, очередь, стрим
ОтветНемедленныйМожет прийти позже или не прийти
СостояниеStateless (обычно)Может быть stateful (позиция в топике)
ДокументацияЭндпоинтыКаналы (топики, очереди)
ПримерыGET /users, POST /ordersuser.created, order.paid

Когда использовать OpenAPI

  • REST API
  • Запрос-ответ
  • Внешние API для партнёров

Когда использовать AsyncAPI

  • Событийно-ориентированные системы
  • Микросервисы с брокером сообщений
  • IoT (MQTT)
  • Стриминг данных (Kafka)
  • Real-time обновления (WebSocket)

Инструменты экосистемы AsyncAPI

ИнструментНазначение
AsyncAPI StudioРедактор спецификации в браузере
AsyncAPI GeneratorГенерация кода, документации, диаграмм
ModelinaГенерация моделей данных (TypeScript, Java, Python)
ParserВалидация и парсинг спецификаций
ReactКомпоненты для встраивания документации
SpectralЛинтер для AsyncAPI

Генерация кода

# Генерация документации (HTML)
asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template -o docs

# Генерация клиента для Kafka на Java
asyncapi generate fromTemplate asyncapi.yaml @asyncapi/java-spring-template -o client

# Генерация моделей TypeScript
asyncapi generate fromTemplate asyncapi.yaml @asyncapi/modelina-template -o models

Лучшие практики

Именование каналов

# Хорошо (иерархия, понятно)
channels:
  user.created:
  user.updated:
  user.deleted:
  order.paid:
  order.shipped:

# Плохо
channels:
  event1:
  user_change:
  msg123:

Версионирование событий

# Вариант 1: в имени канала
channels:
  user.v1.created:
  user.v2.created:

# Вариант 2: в схеме сообщения
components:
  messages:
    UserCreated:
      payload:
        type: object
        properties:
          version:
            type: string
            const: "1.0"

Обязательные заголовки

components:
  messages:
    AnyMessage:
      headers:
        required:
          - messageId
          - timestamp
        properties:
          messageId:
            type: string
            format: uuid
          correlationId:
            type: string
          timestamp:
            type: string
            format: date-time

Документирование ошибок

channels:
  order.created:
    subscribe:
      message:
        oneOf:
          - $ref: '#/components/messages/OrderCreated'
          - $ref: '#/components/messages/OrderCreationFailed'

components:
  messages:
    OrderCreationFailed:
      summary: Ошибка создания заказа
      payload:
        type: object
        properties:
          errorCode:
            type: string
          errorMessage:
            type: string
          originalOrderId:
            type: string

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

Ошибка 1: Путать publish и subscribe

  • publish — сервис отправляет сообщения
  • subscribe — сервис получает сообщения

Решение: Чётко определять, кто публикует, кто подписан.

Ошибка 2: Отсутствие схемы сообщений

Описаны каналы, но не описаны форматы сообщений.

Решение: Всегда описывать payload и headers.

Ошибка 3: Игнорирование версионирования

События меняются, старые подписчики ломаются.

Решение: Версионировать события (в имени канала или в схеме).

Ошибка 4: Слишком много деталей

Спецификация на 5000 строк, которую никто не читает.

Решение: Разбивать на несколько спецификаций (по доменам). Использовать $ref для переиспользования.

Ошибка 5: Нет примеров

Только схемы, без примеров.

Решение: Добавлять examples для каждого сообщения.

Резюме

  1. AsyncAPI — спецификация для документирования асинхронных API (события, очереди, стримы). Это “OpenAPI для Kafka, MQTT, RabbitMQ, WebSocket”.

  2. Основные компоненты: info (метаинформация), servers (брокеры), channels (топики, очереди), operations (publish/subscribe), messages (сообщения), schemas (модели данных).

  3. Ключевое отличие от OpenAPI: OpenAPI описывает синхронные запросы-ответы (HTTP). AsyncAPI описывает асинхронные события (Kafka, MQTT, AMQP, WebSocket).

  4. Когда нужен AsyncAPI: Событийно-ориентированная архитектура, микросервисы с брокером сообщений, IoT (MQTT), стриминг данных (Kafka), real-time обновления (WebSocket).

  5. Что даёт AsyncAPI:

    • Единый контракт для всех сервисов
    • Визуализация потоков событий
    • Генерация кода (клиенты, серверы, модели)
    • Валидация сообщений
    • Документация для команд
  6. Инструменты: AsyncAPI Studio (редактор), AsyncAPI Generator (генерация кода), Modelina (генерация моделей).

  7. Лучшие практики: Именовать каналы как domain.event, версионировать события, добавлять примеры, описывать ошибки.

Проверка знаний

Вопрос 1 из 4
Что в первую очередь описывает AsyncAPI?
Какое ключевое отличие AsyncAPI от OpenAPI подчёркнуто в теме?
Какой элемент AsyncAPI отвечает за место, куда отправляются сообщения?
Какая практика названа хорошей при проектировании AsyncAPI?

Вопросы, где были ошибки