Потоковая передача событий по 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.
curl https://runnev.dev/v1/streams \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"name":"orders-eu","retention_seconds":86400}'
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}
curl -N "https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7?cursor=41800" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $RUNNEV_API_KEY"
Живой поток прямо сейчас
Это публичный демо-поток: доступная только для чтения лента синтетических событий о заказах,
которую наш собственный тестовый стенд публикует круглосуточно. Соединение ниже — это один
долгоживущий GET, открытый прямо в вашем браузере; если он оборвётся, он
переподключится с последнего курсора.
- 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 достаточно полон, чтобы написать собственный клиент за один вечер.