Потоковая передача событий по HTTP

Потоки событий на чистом HTTP

Надёжные упорядоченные потоки, с которыми вы работаете по обычному HTTP. Публикуйте пакет через POST, подписывайтесь долгоживущим GET. Без апгрейда до WebSocket, без собственного протокола, без брокера, который нужно обслуживать.

подписка из терминала
curl -N https://runnev.dev/v1/streams/DQCXqM5x6cySlmtvACNZIm \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer rnv_test_2f8c41a6d90b47e3ba15c7e08d3f6a24"

Вся модель — в трёх запросах

Поток — это упорядоченный надёжный журнал, адресуемый по base62-идентификатору. Издатели дописывают пакеты под последовательными номерами. Подписчики держат один HTTP-ответ открытым и получают пакеты как server-sent events, каждый помечен своим номером, поэтому можно продолжить ровно с того места, где остановились.

Нет брокера, который нужно эксплуатировать, и нечего устанавливать на серверной стороне вашей инфраструктуры. Если что-то умеет делать HTTP-запрос — оно умеет работать с Runnev.

Как работают потоки →

1. создать поток
curl https://runnev.dev/v1/streams \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"name":"orders-eu","retention_seconds":86400}'
2. опубликовать пакет с номером 41823
curl https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7/41823 \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"events":[{"type":"order.paid","id":"o_5521","amount":1999}]}'
# 202  {"seq":41823,"accepted":1,"cursor":41823,"duplicate":false}
3. подписаться и продолжить с курсора
curl -N "https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7?cursor=41800" \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $RUNNEV_API_KEY"

Живой поток прямо сейчас

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

подключение 0 событий
  • 41802 11:02:07.114 128 событий order.paid eu-west
  • 41803 11:02:09.881 116 событий order.created us-east
  • 41804 11:02:12.640 131 событие order.paid ap-south
  • 41805 11:02:15.402 124 события order.refunded eu-west
  • 41806 11:02:18.219 119 событий order.created eu-central

Демо-ключ публичный и ограничен 60 запросами в минуту с одного IP. Он может читать только этот единственный поток. Формат передачи описан в разделе Подписка.

Почему не WebSocket

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

Посредники рвут апгрейды

Корпоративные прямые прокси, некоторые enterprise-фаерволы и отдельные конфигурации CDN отклоняют или неправильно обрабатывают рукопожатие Upgrade: websocket. Обычный HTTP-запрос проходит те же узлы нетронутым.

Serverless не удержит сокет

Функциональные среды тарифицируют работу и завершают её по таймауту, считая реальное время, и никогда не были рассчитаны на удержание сырого сокета. Но они умеют потоково отдавать тело HTTP-ответа — а это всё, что нужно подписчику.

Мобильные сети убивают простаивающие сокеты

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

Мультиплексирование HTTP/2 достаётся даром

Поверх HTTP/2 и HTTP/3 множество параллельных потоков делят одно соединение со сжатием заголовков и управлением потоком, которые вам не пришлось строить. Один подписчик к сорока потокам — это сорок логических потоков на одном сокете.

Это штатный режим — так и задумано

Здоровый подписчик Runnev держит один ответ открытым столько, сколько нужно, а активный издатель отправляет тысячи маленьких POST-запросов в минуту. И то и другое — нормальные, поддерживаемые режимы работы. Платформа, SDK и документация построены вокруг долгоживущих соединений и высокой частоты записи, а не рассматривают их как крайние случаи.

Что вы получаете

Упорядоченные надёжные потоки

У каждого пакета — монотонно растущий номер. Потоки хранятся в окне, которое вы настраиваете по времени и по объёму в байтах.

Возобновление и повтор по курсору

Переподключитесь с ?cursor=N и продолжите ровно после номера N. Повторите любой отдельный пакет по его номеру.

Сырые бинарные пакеты

Публикуйте application/octet-stream — мы никогда не заглядываем внутрь. Приносите protobuf, msgpack, Avro или собственный формат кадров.

Идемпотентные публикации

Записи привязаны к ключу (stream, seq). Повторите публикацию — вторая попытка вернёт duplicate: true, а не вторую копию.

HTTP/2 и HTTP/3

Доставка по современному HTTP на границе сети; при этом сохранён и старый потоковый путь HTTP/1.1 для клиентов, которым он нужен.

Три SDK, без брокера

Клиенты без зависимостей для JavaScript, Python и Go. Нечего разворачивать, не нужно нянчить очередь и держать ZooKeeper.

Чем Runnev не является

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

  • Нет подтверждений от каждого потребителя. Подписчики сами отслеживают свой курсор. На стороне сервера нет отметки «этот потребитель обработал пакет N» и нет повторной доставки конкретному потребителю.
  • Нет упорядочивания между потоками. Порядок гарантируется внутри одного потока. У двух событий из двух разных потоков нет определённого взаимного порядка.
  • Нет exactly-once. Доставка — не менее одного раза. Поскольку публикации идемпотентны по (stream, seq), дедупликация на стороне чтения — это проверка в одну строку, и мы подробно описываем, как её сделать.
  • Пакет ограничен 8 МиБ. Тело одной публикации больше 8 МиБ отклоняется с 413. Разбивайте крупные полезные нагрузки по номерам.

Полный набор гарантий →

Вопросы

Нужно ли запускать брокер или агента?

Нет. Runnev — это размещённый HTTP API. Ваши издатели делают POST-запросы, а подписчики — один длинный GET. На вашей стороне нечего устанавливать, кроме необязательного SDK, а сами SDK — тонкие обёртки над теми же HTTP-вызовами.

Чем это отличается от server-sent events на моём собственном сервере?

Сторона подписки и есть server-sent events; это сделано намеренно, потому что SSE — это обычный HTTP, который проходит везде. Runnev добавляет за ним надёжный упорядоченный журнал: последовательные номера, возобновление по курсору, повтор, срок хранения и идемпотентные записи, — так что подписчик, отключившийся на час, догоняет, а не теряет всё.

Что происходит, когда подписчик отстаёт?

С издателем не происходит ничего. Пакеты буферизуются в потоке в пределах окна хранения. Медленный подписчик читает в своём темпе; если он отключился — возобновляет с курсора. Если он отстал за пределы окна хранения, вытесненные пакеты пропускаются, и разрыв виден как скачок в номерах.

Можно ли публиковать бинарные данные?

Да. Создайте поток в режиме raw и публикуйте тела application/octet-stream. Мы храним и доставляем байты дословно и никогда их не разбираем. См. Публикацию.

Тарифицируются ли открытые соединения подписки?

Нет. Оплата идёт за опубликованные события и исходящие байты. Держать подписку открытой, даже сутками, само по себе ничего не стоит. См. Тарифы.

Какие языки поддерживаются?

Любой язык с HTTP-клиентом. Мы выпускаем официальные SDK для JavaScript, Python и Go, а справочник API достаточно полон, чтобы написать собственный клиент за один вечер.