RSocket – это прикладной протокол для мультиплексированной дуплексной связи по протоколам TCP, WebSocket и другим механизмам передачи байтовых потоков, использующий одну из следующих моделей взаимодействия:

  • Request-Response – отправка одного сообщения и получение одного в ответ.

  • Request-Stream – отправка одного сообщения и получение потока сообщений в ответ.

  • Channel – передача потоков сообщений в обоих направлениях.

  • Fire-and-Forget– отправка одностороннего сообщения.

После установления первоначального соединения различие между "клиентом" и "сервером" теряется, поскольку обе стороны становятся симметричными, и каждая сторона может инициировать одно из вышеперечисленных взаимодействий. Именно поэтому в протоколе участвующие стороны называются "реквестер (запросчик)" и "респондер (ответчик)", а вышеуказанные взаимодействия – "потоки запросов" или просто "запросы".

Таковы основные особенности и преимущества протокола RSocket:

  • Семантика Reactive Streams по границе сети – для потоковых запросов, таких как Request-Stream и Channel, сигналы обратной реакции проходят между реквестером и респондером, позволяя реквестеру замедлить респондер у источника, что снижает зависимость от контроля перегрузок на сетевом уровне, а также необходимость буферизации на сетевом уровне или на любом другом уровне.

  • Регулирование (дросселирование запросов) – эта функция названа "Leasing (ограниченное пользование)" по имени фрейма LEASE, который может быть отправлен с любой стороны для ограничения общего количества запросов, разрешенных другой стороной за определенное время. Периоды ограниченного использования периодически продлеваются.

  • Возобновление сеанса – предназначено для случаев потери связи и требует сохранения определенного состояния. Управление состоянием для приложений организовано прозрачно и хорошо работает в сочетании с обратной реакцией, которая может остановить поставщик, когда это возможно, и уменьшить величину требуемого состояния.

  • Фрагментация и повторная сборка больших сообщений.

  • Keepalive (heartbeat-сообщения).

В RSocket есть реализации на нескольких языках. Библиотека Java построена на основе Project Reactor, а для передачи используется Reactor Netty. Это означает, что сигналы от публикаторов Reactive Streams в вашем приложении будут прозрачно распространяться через RSocket по сети.

Протокол

Одним из преимуществ RSocket является то, что он имеет четко прописанную логику работы "на лету" и удобочитаемую спецификацию, а также некоторые расширения протокола. Поэтому разумно будет прочитать спецификацию, независимо от реализации языка и API фреймворков более высокого уровня. В данном разделе представлено краткое описание создания определенного контекста.

Подключение

Изначально клиент подключается к серверу через низкоуровневое потоковое средство передачи, такое как протокол TCP или WebSocket, и посылает серверу фрейм SETUP для установки параметров соединения.

Сервер может отклонить фрейм SETUP, но обычно после его отправки (для клиента) и получения (для сервера) обе стороны могут начать посылать запросы, если только SETUP не указывает на использование семантики ограниченного использования для ограничения количества запросов, и в этом случае обе стороны должны дождаться фрейма LEASE с другой стороны, чтобы разрешить делать запросы.

Выполнение запросов

После установления соединения обе стороны могут инициировать запрос через один из фреймов REQUEST_RESPONSE, REQUEST_STREAM, REQUEST_CHANNEL или REQUEST_FNF. Каждый из этих фреймов несет одно сообщение от реквестера к респондеру.

респондер может затем вернуть фреймы PAYLOAD с ответными сообщениями, а в случае REQUEST_CHANNEL реквестер может также отпарвлять фреймы PAYLOAD с дополнительными сообщениями-запросами.

Если запрос включает в себя поток сообщений, таких как Request-Stream и Channel, респондер обязан следовать сигналам запроса от реквестера. Запрос выражается в количестве сообщений. Начальный запрос указывается в фреймах REQUEST_STREAM и REQUEST_CHANNEL. Последующие запросы сигнализируются с помощью фреймов REQUEST_N.

Каждая сторона также может отправлять через фрейм METADATA_PUSH уведомления о метаданных, которые относятся не к отдельному запросу, а к соединению в целом.

Формат сообщения

Сообщения RSocket содержат данные и метаданные. Метаданные могут использоваться для отправки маршрута, токена безопасности и т.д. Данные и метаданные могут быть по-разному отформатированы. MIME-типы для каждого из них объявляются в фрейме SETUP и применяются ко всем запросам для данного соединения.

Хотя все сообщения могут содержать метаданные, обычно метаданные, такие как маршрут, предоставляются по каждому запросу и, следовательно, включаются только в первое сообщение запроса, то есть в один из фреймов REQUEST_RESPONSE, REQUEST_STREAM, REQUEST_CHANNEL или REQUEST_FNF.

Расширения протокола определяют общие форматы метаданных для использования в приложениях:

Реализация Java

Java-реализация для RSocket построена на основе Project Reactor. Механизмы передачи для TCP и WebSocket построены на базе Reactor Netty. Будучи библиотекой Reactive Streams, Reactor упрощает работу по реализации протокола. В приложениях вполне естественным является использование Flux и Mono с декларативными операторами и прозрачной поддержкой обратной реакции.

API в RSocket для Java намеренно упрощен и является базовым. В нем особенное внимание уделено особенностям протокола и сохранению модели прикладного программирования (например, генератор кода для RPC в сравнении с другими) в качестве независимой функциональности более высокого уровня.

Основной контракт io.rsocket.RSocket моделирует четыре типа взаимодействия запросов, при этом Mono представляет промис одного сообщения, Flux – поток сообщений, а io.rsocket.Payload – само сообщение с доступом к данным и метаданным в виде байтовых буферов. Контракт RSocket используется симметрично. В части отправки запросов приложению предоставляется RSocket для выполнения запросов. В части отправки ответов приложение реализует RSocket для обработки запросов.

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

Средства поддержки в Spring

Модуль spring-messaging содержит следующее:

  • RSocketRequester – текучий API для выполнения запросов через io.rsocket.RSocket с кодированием/декодированием данных и метаданных.

  • Аннотированные респондеры – аннотированные с помощью @MessageMapping методы обработчика для передачи ответа.

Модуль spring-web содержит реализации Encoder и Decoder, такие как Jackson CBOR/JSON и Protobuf, которые, скорее всего, понадобятся приложениям на RSocket. Он также содержит PathPatternParser, который можно подключить для эффективного сопоставления маршрута.

Spring Boot 2.2 поддерживает создание сервера RSocket через протоколы TCP или WebSocket, включая возможность использования RSocket через WebSocket в сервере WebFlux. Также имеется поддержка клиента и автоконфигурация для RSocketRequester.Builder и RSocketStrategies. Более подробную информацию см. в разделе, посвященном RSocket в справочнике по Spring Boot.

Spring Security 5.2 предусматривает поддержку RSocket.

Spring Integration 5.2 предусматривает входящие и исходящие шлюзы для взаимодействия с клиентами и серверами RSocket. Более подробную информацию см. в справочном руководстве Spring Integration Reference Manual.

Spring Cloud Gateway поддерживает соединения на основе RSocket.