skip to content

RSocket Messaging

RSocket in Spring: the four interaction models with reactive types, the requester and mapping annotations, plus security and transport options. Interviewers raise it when the topic is streaming or bidirectional communication between services.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

10

What are the four RSocket interaction models, and how does each map to Reactor return types on a Spring @MessageMapping handler?

level: juniorimportance: must knowfreq 70%

answer

  1. 1-in-1-out, 1-in-0-out, 1-in-N-out, N-in-N-out
  2. Mono vs Flux return picks single vs stream
  3. Flux parameter = channel
  4. retrieveMono / retrieveFlux / send()
  5. route() = routing metadata, not a URL

basics

~20 s

Request-response (one in, one out), fire-and-forget (one in, no reply), request-stream (one in, many out), request-channel (many in, many out). In Spring: return Mono for the first two, Flux for streams; a Flux parameter means channel.

solid answer

~40 s

RSocket defines four interaction models. Request-response: send one message, get one back — a handler returns Mono<T>. Fire-and-forget: send one message, expect nothing — handler returns Mono<Void> or void. Request-stream: send one message, receive a stream — handler returns Flux<T>. Request-channel: bidirectional streams — the handler takes a Flux<T> parameter and returns a Flux<T>. Spring's @MessageMapping infers the model from the method signature: the return type (Mono vs Flux) picks single-vs-stream response, and a Flux argument signals a channel. On the client, RSocketRequester mirrors this: retrieveMono() for request-response, retrieveFlux() for streams, and send() for fire-and-forget. This symmetry means the same route can be driven correctly only if both sides agree on the model.

code

java · 28 lines
java
@Controller
class QuoteController {

    // request-response: Mono in, Mono out
    @MessageMapping("price.{symbol}")
    Mono<Quote> price(@DestinationVariable String symbol) {
        return Mono.just(new Quote(symbol, 42.0));
    }

    // fire-and-forget: no response
    @MessageMapping("metrics")
    Mono<Void> record(Metric m) {
        return metrics.save(m).then();
    }

    // request-stream: one in, many out
    @MessageMapping("quotes.{symbol}")
    Flux<Quote> quotes(@DestinationVariable String symbol) {
        return Flux.interval(Duration.ofSeconds(1))
                   .map(i -> new Quote(symbol, 42.0 + i));
    }

    // request-channel: Flux in, Flux out
    @MessageMapping("chat")
    Flux<Message> chat(Flux<Message> incoming) {
        return incoming.map(Message::echo);
    }
}

go deeper

for a junior

Name the four models and match Mono→single, Flux→stream, void→fire-and-forget.

for a middle

Explain how the method signature (return type and Flux parameter) selects the model, plus the matching RSocketRequester terminal calls.

for a senior

Discuss backpressure over REQUEST_N frames, cold/lazy publishers not sending until subscribed, and mismatch behavior.

for a principal

Frame model choice as an API design decision (delivery guarantees, resource cost, contract symmetry between both peers).

RSocket is a binary, message-driven application protocol built on Reactive Streams semantics (backpressure included). Unlike HTTP's single request/response shape, it defines **four interaction models**, and Spring Messaging exposes all four through annotations and the `RSocketRequester` fluent client. **The four models** 1. **Request-Response** — the client sends one payload and receives exactly one payload back. This is the HTTP-like case. Spring handler returns `Mono<T>`; the client calls `retrieveMono(...)`. 2. **Fire-and-Forget** — the client sends one payload and the server sends *no* response at all (not even an acknowledgement of completion at the app level). Useful for high-volume, lossy-tolerant signals (metrics, logs). Spring handler returns `Mono<Void>` or `void`; the client calls `send()` (which itself returns a `Mono<Void>` that completes when the frame is written, not when processed). 3. **Request-Stream** — the client sends one payload and receives a stream of zero-to-many payloads back, with backpressure. Spring handler returns `Flux<T>`; the client calls `retrieveFlux(...)`. 4. **Request-Channel** — a fully bidirectional stream: both sides send a stream of payloads. Spring handler declares a `Flux<T>` (or `Publisher<T>`) **parameter** and returns a `Flux<T>`; the client passes a `Flux` to `.data(...)` and calls `retrieveFlux(...)`. **How Spring picks the model.** With `@MessageMapping`, you never name the interaction model explicitly. Spring's `RSocketMessageHandler` infers it from the method signature: - Return `Flux` ⇒ streaming response; return `Mono`/plain value ⇒ single response; return `Mono<Void>`/`void` ⇒ no response (fire-and-forget). - A streaming **parameter** (`Flux`) ⇒ request-channel. **The client side — `RSocketRequester`.** It is a thin, fluent, reactive client: ``` requester.route("prices.{symbol}", "AAPL") // route metadata .data(request) // payload (may be a Publisher) .retrieveFlux(Quote.class); // chooses interaction model ``` The *terminal* method chooses the model: `retrieveMono` (request-response), `retrieveFlux` (request-stream, or request-channel when `data(...)` was given a `Flux`), and `send()` (fire-and-forget). If you never subscribe to the returned publisher, **nothing is sent** — these are cold, lazy publishers. **Route metadata.** RSocket has no URLs; routing is carried in frame *metadata*. Spring encodes the route via composite metadata using the well-known mime type `message/x.rsocket.routing.v0` (`WellKnownMimeType.MESSAGE_RSOCKET_ROUTING`). `route("a.b.{id}", var)` expands template variables and attaches this metadata; the responder matches it against `@MessageMapping("a.b.{id}")` patterns (Ant-style, with `{}` variables bound via `@DestinationVariable`). **Edge cases & gotchas.** - Fire-and-forget's `send()` completing does **not** mean the server processed (or even received) the message — there is no application-level reply. - Mismatching models breaks silently-ish: calling `retrieveMono` against a handler that streams gives you only the first element; calling `retrieveFlux` on a request-response route yields a single-element flux. - Request-stream and request-channel honor Reactive Streams backpressure over the wire via RSocket REQUEST_N frames — a slow consumer throttles the producer. - `@ConnectMapping` is a *fifth*, connection-level hook (not an interaction model): it handles the setup payload/metadata at connect time and must return `void`/`Mono<Void>` — it cannot reply with data. **When to use which.** Request-response for RPC-style calls; fire-and-forget for cheap one-way signals; request-stream for server push / subscriptions; request-channel for interactive, long-lived bidirectional exchanges (e.g., live collaboration, flow-controlled uploads).

  • How does Spring know a handler is request-channel rather than request-stream?
    By the parameter type: a streaming input parameter (Flux/Publisher of the payload) makes it request-channel. Request-stream takes a single payload and returns Flux.
  • Which client method triggers fire-and-forget, and what does its Mono<Void> signal?
    RSocketRequester...send(). Its Mono<Void> completes when the frame has been written/sent, not when the server received or processed it — there is no app-level reply.

saying these in an interview costs you the question

  • Thinking fire-and-forget still returns an acknowledgement or completion signal from the server
  • Claiming you specify the interaction model with an annotation attribute rather than by return/parameter type
  • Believing RSocket routes are URLs rather than frame metadata

context

open as a page

What does @EnableRSocketSecurity do, and how do you turn on security for an RSocket server in Spring?

level: juniorimportance: must knowfreq 45%

basics

~10 s

@EnableRSocketSecurity turns on Spring Security for RSocket and gives you an RSocketSecurity builder. You use it to create a PayloadSocketAcceptorInterceptor bean that defines who can connect and which routes require authentication.

open as a page

How do you build and use an RSocketRequester in Spring — establishing a connection, attaching route metadata, and choosing the interaction model?

level: middleimportance: should knowfreq 55%

basics

~10 s

Autowire the RSocketRequester.Builder, connect with tcp()/websocket() to get an RSocketRequester. Then call route("path"), data(payload), and a terminal like retrieveMono(), retrieveFlux(), or send() — the terminal picks the interaction model.

open as a page

Explain the difference between securing the SETUP payload and securing REQUEST payloads in RSocket, and how you express each with RSocketSecurity.

level: middleimportance: should knowfreq 35%

basics

~10 s

The SETUP payload authenticates the connection once, when the client establishes it. REQUEST payloads authenticate individual messages/routes. In the authorizePayload DSL you use .setup() for the connection and .route(...)/.anyRequest() for per-message rules.

open as a page

Explain request-channel in Spring RSocket: how the handler is shaped, how backpressure flows both ways, and when to choose it over request-stream.

level: seniorimportance: should knowfreq 35%

basics

~20 s

Request-channel is a two-way stream. The @MessageMapping handler takes a Flux parameter (inbound stream) and returns a Flux (outbound stream); the client passes a Flux to data() and calls retrieveFlux(). Both directions are independent, backpressured Reactive Streams. Choose it when the client must keep sending while receiving.

open as a page

What is @ConnectMapping in Spring RSocket, how does it differ from @MessageMapping, and what are its constraints?

level: seniorimportance: should knowfreq 40%

basics

~20 s

@ConnectMapping handles the RSocket SETUP frame once, when a client connects — good for auth, registration, or reading setup metadata. Unlike @MessageMapping (per-request, any interaction model), it runs once per connection and cannot return data; it returns void/Mono<Void>.

open as a page

How does a Spring RSocket requester present authentication metadata (simple username/password vs bearer/JWT), and how does the server validate it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The requester attaches auth metadata using a well-known RSocket mime type. For simple auth it sends a UsernamePasswordMetadata; for bearer it sends a BearerTokenMetadata (a JWT). The server enables simpleAuthentication() or jwt() on RSocketSecurity to decode and validate it.

open as a page

How do you authorize specific @MessageMapping routes in RSocket — via the authorizePayload DSL vs @PreAuthorize method security — and what are the trade-offs?

level: seniorimportance: should knowfreq 25%

basics

~10 s

You can authorize routes centrally in RSocketSecurity.authorizePayload using .route("pattern").hasRole(...), or on the handler with @PreAuthorize after enabling @EnableReactiveMethodSecurity. The DSL is connection-layer; method security is per-method and closer to the code.

open as a page

As an architect, how do you choose among the four RSocket interaction models, and what delivery/error guarantees does each imply — especially fire-and-forget?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Match the model to the message shape: one reply → request-response; no reply, best-effort → fire-and-forget; server pushes many → request-stream; both sides stream → request-channel. Fire-and-forget gives no app-level acknowledgement or error back, so use it only for tolerable-loss signals.

open as a page

Compare RSocket's TCP and WebSocket transports in Spring, and explain what session resumption (resume) is and how it interacts with security.

level: principalimportance: nice to knowfreq 18%

basics

~20 s

RSocket runs over TCP or WebSocket. TCP is a raw framed socket (fast, internal); WebSocket tunnels through HTTP (works through browsers/proxies/firewalls). Resume lets a dropped connection reconnect and continue the same session without losing in-flight streams, using a resume token.

open as a page