RSocket with Spring Integration¶
Spring Integration adds first-class RSocket support to its messaging model: an RSocket server can act as an inbound gateway in an Integration flow, and an Integration flow can drive an outbound RSocket gateway.
Getting started¶
Alongside the RSocket starter, add Spring Integration and its RSocket support:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
The server exposes an RSocket endpoint over TCP on port 7000:
spring.rsocket.server.port=7000
spring.rsocket.server.transport=tcp
# auto configure spring integration rsocket server.
spring.integration.rsocket.server.messageMappingEnabled=true
The server side: an inbound gateway¶
The application declares a ServerRSocketConnector and an IntegrationFlow with an RSocket inbound gateway. The flow receives a Flux<String> on the /uppercase route and transforms every element to uppercase:
@SpringBootApplication
@EnableIntegration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// see PR: https://github.com/spring-projects/spring-boot/pull/18834
@Bean
ServerRSocketMessageHandler serverRSocketMessageHandler(RSocketStrategies rSocketStrategies) {
var handler = new ServerRSocketMessageHandler(true);
handler.setRSocketStrategies(rSocketStrategies);
return handler;
}
@Bean
public ServerRSocketConnector serverRSocketConnector(ServerRSocketMessageHandler serverRSocketMessageHandler) {
return new ServerRSocketConnector(serverRSocketMessageHandler);
}
@Bean
public IntegrationFlow rsocketUpperCaseFlow(ServerRSocketConnector serverRSocketConnector) {
return IntegrationFlow
.from(RSockets.inboundGateway("/uppercase")
.requestElementType(ResolvableType.forClass(String.class))
.interactionModels(RSocketInteractionModel.requestChannel)
.rsocketConnector(serverRSocketConnector)
)
.<Flux<String>, Flux<String>>transform((flux) -> flux.map(String::toUpperCase))
.get();
}
}
Reading the wiring:
- The custom
ServerRSocketMessageHandlershares the Boot auto-configuredRSocketStrategies(including codecs) with Integration's message handler. RSockets.inboundGateway("/uppercase")maps the/uppercaseroute to the flow;interactionModels(requestChannel)narrows it to the channel model, which means aFluxin and aFluxout.- The
transformstep uppercases every incoming element. After that, any Integration DSL step such as.filter,.split, or.enrichHeaderscan follow.
Note
Spring Integration 7 removed the IntegrationFlows factory class. Declare the flows with the static IntegrationFlow.from(...) methods instead, as shown above.
The client side: an outbound gateway¶
The client example declares a ClientRSocketConnector pointing at the server and an outbound gateway flow that exposes the remote call as a Function<Flux<String>, Flux<String>>:
@Bean
public ClientRSocketConnector clientRSocketConnector() {
ClientRSocketConnector clientRSocketConnector = new ClientRSocketConnector("localhost", 7000);
clientRSocketConnector.setAutoStartup(false);
return clientRSocketConnector;
}
@Bean
public IntegrationFlow rsocketUpperCaseRequestFlow(ClientRSocketConnector clientRSocketConnector) {
return IntegrationFlow
.from(Function.class)
.handle(RSockets.outboundGateway("/uppercase")
.interactionModel((message) -> RSocketInteractionModel.requestChannel)
.expectedResponseType("T(java.lang.String)")
.clientRSocketConnector(clientRSocketConnector))
.get();
}
In a single Spring Boot application, the server connector binds port 7000 and the client connector (with setAutoStartup(false)) starts on demand. The result is a bean of type Function<Flux<String>, Flux<String>> that other components can inject and call directly.
Testing¶
To test the outbound gateway, start the client connector, apply the gateway function, and verify the uppercased channel result:
@SpringBootTest
public class IntegrationTests {
@Autowired
@Qualifier("rsocketUpperCaseRequestFlow.gateway")
Function<Flux<String>, Flux<String>> uppercaseFlow;
@Autowired
ClientRSocketConnector clientRSocketConnector;
@BeforeEach
public void startClient() {
if (!clientRSocketConnector.isRunning()) {
clientRSocketConnector.start();
}
}
@Test
public void testUppercaseFlow() {
uppercaseFlow.apply(Flux.just("a", "b", "c", "d"))
.as(StepVerifier::create)
.expectNext("A", "B", "C", "D")
.verifyComplete();
}
}
See the complete example and tests in integration-rsocket.