Document RSocket support
* Fix some typos in `webflux.adoc` and `dsl.adoc` * Make `FunctionsTests.kt` more Kotlin friendly * Improve RSocket components and tests for them, especially rely on the default `RSocketStrategies` in the target SF RSocket components * Rework `ServerRSocketConnector.setClientRSocketKeyStrategy` to use the whole `MessageHeaders` for consultation. It turns out that just destination is not enough since it can be used from different clients * Make the key based on the provided `setupData` by default * Include all the `MessageHeaders` into the `RSocketConnectedEvent` * Improve Docs according review feedback and Docs in SF Doc polishing
This commit is contained in:
committed by
Gary Russell
parent
21335812a3
commit
b12657c599
@@ -1142,7 +1142,7 @@ In this case, the message handler for the first flow can be referenced with bean
|
||||
NOTE: An `id` attribute is required when you usE `useFlowIdAsPrefix()`.
|
||||
|
||||
[[java-dsl-gateway]]
|
||||
=== `IntegrationFlow` as Gateway
|
||||
=== `IntegrationFlow` as a Gateway
|
||||
|
||||
The `IntegrationFlow` can start from the service interface that provides a `GatewayProxyFactoryBean` component, as the following example shows:
|
||||
|
||||
|
||||
328
src/reference/asciidoc/rsocket.adoc
Normal file
328
src/reference/asciidoc/rsocket.adoc
Normal file
@@ -0,0 +1,328 @@
|
||||
[[rsocket]]
|
||||
== RSocket Support
|
||||
|
||||
The RSocket Spring Integration module (`spring-integration-rsocket`) allows for executions of https://rsocket.io/[RSocket application protocol].
|
||||
|
||||
You need to include this dependency into your project:
|
||||
|
||||
====
|
||||
.Maven
|
||||
[source, xml, subs="normal"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-rsocket</artifactId>
|
||||
<version>{project-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
.Gradle
|
||||
[source, groovy, subs="normal"]
|
||||
----
|
||||
compile "org.springframework.integration:spring-integration-rsocket:{project-version}"
|
||||
----
|
||||
====
|
||||
|
||||
This module is available starting with version 5.2 and is based on the Spring Messaging foundation with its RSocket component implementations, such as `RSocketRequester`, `RSocketMessageHandler` and `RSocketStrategies`.
|
||||
See https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#rsocket[Spring Framework RSocket Support] for more information about the RSocket protocol, terminology and components.
|
||||
|
||||
Before starting an integration flow processing via channel adapters, we need to establish an RSocket connection between server and client.
|
||||
For this purpose, Spring Integration RSocket support provides the `ServerRSocketConnector` and `ClientRSocketConnector` implementations of the `AbstractRSocketConnector`.
|
||||
|
||||
The `ServerRSocketConnector` exposes a listener on the host and port according to provided `io.rsocket.transport.ServerTransport` for accepting connections from clients.
|
||||
An internal `RSocketFactory.ServerRSocketFactory` instance can be customized with the `setFactoryConfigurer()`, as well as other options that can be configured, e.g. `RSocketStrategies` and `MimeType` for payload data and headers metadata.
|
||||
When a `setupRoute` is provided from the client requester (see `ClientRSocketConnector` below), a connected client is stored as a `RSocketRequester` under the key determined by the `clientRSocketKeyStrategy` `BiFunction<Map<String, Object>, DataBuffer, Object>`.
|
||||
By default a connect data is used for the key as a converted value to string with UTF-8 charset.
|
||||
Such an `RSocketRequester` registry can be used in the application logic to determine a particular client connection for interaction with it, or for publishing the same message to all connected clients.
|
||||
When a connection is established from the client, an `RSocketConnectedEvent` is emitted from the `ServerRSocketConnector`.
|
||||
This is similar to what is provided by the `@ConnectMapping` annotation in Spring Messaging module.
|
||||
The mapping pattern `*` means accept all the client routes.
|
||||
The `RSocketConnectedEvent` can be used to distinguish different routes via `DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER` header.
|
||||
|
||||
A typical server configuration might look like this:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder()
|
||||
.decoder(StringDecoder.textPlainOnly())
|
||||
.encoder(CharSequenceEncoder.allMimeTypes())
|
||||
.dataBufferFactory(new DefaultDataBufferFactory(true))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServerRSocketConnector serverRSocketConnector() {
|
||||
ServerRSocketConnector serverRSocketConnector = new ServerRSocketConnector("localhost", 0);
|
||||
serverRSocketConnector.setRSocketStrategies(rsocketStrategies());
|
||||
serverRSocketConnector.setMetadataMimeType(new MimeType("message", "x.rsocket.routing.v0"));
|
||||
serverRSocketConnector.setFactoryConfigurer((factory) -> factory.frameDecoder(PayloadDecoder.ZERO_COPY));
|
||||
serverRSocketConnector.setClientRSocketKeyStrategy((headers, data) -> ""
|
||||
+ headers.get(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER));
|
||||
return serverRSocketConnector;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onApplicationEvent(RSocketConnectedEvent event) {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
All the options, including `RSocketStrategies` bean and `@EventListener` for `RSocketConnectedEvent`, are optional.
|
||||
See `ServerRSocketConnector` JavaDocs for more information.
|
||||
|
||||
The `ClientRSocketConnector` serves as a holder for `RSocketRequester` based on the `RSocket` connected via the provided `ClientTransport`.
|
||||
The `RSocketFactory.ClientRSocketFactory` can be customized with the provided `ClientRSocketFactoryConfigurer`.
|
||||
The `setupRoute` (with optional templates variables) and `setupData` with metadata can be also configured on this component.
|
||||
|
||||
A typical client configuration might look like this:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder()
|
||||
.decoder(StringDecoder.textPlainOnly())
|
||||
.encoder(CharSequenceEncoder.allMimeTypes())
|
||||
.dataBufferFactory(new DefaultDataBufferFactory(true))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientRSocketConnector clientRSocketConnector() {
|
||||
ClientRSocketConnector clientRSocketConnector =
|
||||
new ClientRSocketConnector("localhost", serverRSocketConnector().getBoundPort().block());
|
||||
clientRSocketConnector.setRSocketStrategies(rsocketStrategies());
|
||||
clientRSocketConnector.setSetupRoute("clientConnect/{user}");
|
||||
clientRSocketConnector.setSetupRouteVariables("myUser");
|
||||
return clientRSocketConnector;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Most of these options (including `RSocketStrategies` bean) are optional.
|
||||
Note how we connect to the locally started RSocket server on the arbitrary port.
|
||||
See `ServerRSocketConnector.clientRSocketKeyStrategy` for `setupData` use cases.
|
||||
Also see `ClientRSocketConnector` and its `AbstractRSocketConnector` superclass JavaDocs for more information.
|
||||
|
||||
Both `ClientRSocketConnector` and `ServerRSocketConnector` are responsible for mapping inbound channel adapters to their `path` configuration for routing incoming RSocket requests.
|
||||
See the next section for more information.
|
||||
|
||||
[[rsocket-inbound]]
|
||||
=== RSocket Inbound Gateway
|
||||
|
||||
The `RSocketInboundGateway` is responsible for receiving RSocket requests and producing responses (if any).
|
||||
It requires an array of `path` mapping which could be as patterns similar to MVC request mapping or `@MessageMapping` semantics.
|
||||
Such a bean, according its `IntegrationRSocketEndpoint` implementation (extension of a `ReactiveMessageHandler`), is auto detected either by the `ServerRSocketConnector` or `ClientRSocketConnector` for a routing logic in the internal `IntegrationRSocketMessageHandler` for incoming requests.
|
||||
An `AbstractRSocketConnector` can be provided to the `RSocketInboundGateway` for explicit endpoint registration.
|
||||
This way, the auto-detection option is disabled on that `AbstractRSocketConnector`.
|
||||
The `RSocketStrategies` can also be injected into the `RSocketInboundGateway` or they are obtained from the provided `AbstractRSocketConnector` overriding any explicit injection.
|
||||
Decoders are used from those `RSocketStrategies` to decode a request payload according to the provided `requestElementType`.
|
||||
If an `RSocketPayloadReturnValueHandler.RESPONSE_HEADER` header is not provided in incoming the `Message`, the `RSocketInboundGateway` treats a request as a `fireAndForget` RSocket interaction model.
|
||||
In this case, an `RSocketInboundGateway` performs a plain `send` operation into the `outputChannel`.
|
||||
Otherwise a `MonoProcessor` value from the `RSocketPayloadReturnValueHandler.RESPONSE_HEADER` header is used for sending a reply to the RSocket.
|
||||
For this purpose, an `RSocketInboundGateway` performs a `sendAndReceiveMessageReactive` operation on the `outputChannel`.
|
||||
The `payload` of the message to send downstream is always a `Flux` according to `MessagingRSocket` logic.
|
||||
When in a `fireAndForget` RSocket interaction model, the messsage has a plain converted `payload`.
|
||||
The reply `payload` could be a plain object or a `Publisher` - the `RSocketInboundGateway` converts both of them properly into an RSocket response according to the encoders provided in the `RSocketStrategies`.
|
||||
|
||||
See <<rsocket-java-config>> for samples how to configure an `RSocketInboundGateway` endpoint and deal with payloads downstream.
|
||||
|
||||
[[rsocket-outbound]]
|
||||
=== RSocket Outbound Gateway
|
||||
|
||||
The `RSocketOutboundGateway` is an `AbstractReplyProducingMessageHandler` to perform requests into RSocket and produce replies based on the RSocket replies (if any).
|
||||
A low level RSocket protocol interaction is delegated into an `RSocketRequester` resolved from the provided `ClientRSocketConnector` or from the `RSocketRequesterMethodArgumentResolver.RSOCKET_REQUESTER_HEADER` header in the request message on the server side.
|
||||
A target `RSocketRequester` on the server side can be resolved from an `RSocketConnectedEvent` or using `ServerRSocketConnector.getClientRSocketRequester()` API according some business key selected for connect request mappings via `ServerRSocketConnector.setClientRSocketKeyStrategy()`.
|
||||
See `ServerRSocketConnector` JavaDocs for more information.
|
||||
|
||||
The `route` to send request has to be configured explicitly (together with path variables) or via a SpEL expression which is evaluated against request message.
|
||||
|
||||
The RSocket communication command can be provided via `RSocketOutboundGateway.Command` option or respective expression setting.
|
||||
By default a `requestResponse` is used for common gateway use-cases.
|
||||
|
||||
When request message payload is a `Publisher`, a `publisherElementType` option can be provided to encode its elements according an `RSocketStrategies` supplied in the target `RSocketRequester`.
|
||||
An expression for this option can evaluate to a `ParameterizedTypeReference`.
|
||||
See the `RSocketRequester.RequestSpec.data()` JavaDocs for more information about data and its type.
|
||||
|
||||
An RSocket request can also be enhanced with a `metadata`.
|
||||
For this purpose a `metadataExpression` against request message can be configured on the `RSocketOutboundGateway`.
|
||||
Such an expression must evaluate to a `Map<Object, MimeType>`.
|
||||
|
||||
When `command` is not `fireAndForget`, an `expectedResponseType` must be supplied.
|
||||
It is a `String.class` by default.
|
||||
An expression for this option can evaluate to a `ParameterizedTypeReference`.
|
||||
See `RSocketRequester.RequestSpec.retrieveMono()` and `RSocketRequester.RequestSpec.retrieveFlux()` JavaDocs for more information about reply data and its type.
|
||||
|
||||
A reply `payload` from the `RSocketOutboundGateway` is always `Mono` (even for a `fireAndForget` command it is `Mono<Void>`) always making this component as `async`.
|
||||
Such a `Mono` is subscribed before producing into the `outputChannel` for regular channels or processed on demand by the `FluxMessageChannel`.
|
||||
A `Flux` response for the `requestStreamOrChannel` command is also wrapped into a mentioned reply `Mono`.
|
||||
It can be flatten downstream by the `FluxMessageChannel` with a passthrough service activator:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ServiceActivator(inputChannel = "rsocketReplyChannel", outputChannel ="fluxMessageChannel")
|
||||
public Flux<?> flattenRSocketResponse(Flux<?> payload) {
|
||||
return payload;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Or subscribed explicitly in the target application logic.
|
||||
|
||||
See <<rsocket-java-config>> for samples how to configure an `RSocketOutboundGateway` endpoint a deal with payloads downstream.
|
||||
|
||||
[[rsocket-namespace]]
|
||||
=== RSocket Namespace Support
|
||||
|
||||
Spring Integration provides an `rsocket` namespace and the corresponding schema definition.
|
||||
To include it in your configuration, add the following namespace declaration in your application context configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-rsocket="http://www.springframework.org/schema/integration/rsocket"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/rsocket
|
||||
https://www.springframework.org/schema/integration/rsocket/spring-integration-rsocket.xsd">
|
||||
...
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
==== Inbound
|
||||
|
||||
To configure Spring Integration RSocket inbound channel adapters with XML, you need to use an appropriate `inbound-gateway` components from the `int-rsocket` namespace.
|
||||
The following example shows how to configure it:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int-rsocket:inbound-gateway id="inboundGateway"
|
||||
path="testPath"
|
||||
rsocket-connector="clientRSocketConnector"
|
||||
request-channel="requestChannel"
|
||||
rsocket-strategies="rsocketStrategies"
|
||||
request-element-type="byte[]"/>
|
||||
----
|
||||
====
|
||||
|
||||
A `ClientRSocketConnector` and `ServerRSocketConnector` should be configured as generic `<bean>` definitions.
|
||||
|
||||
==== Outbound
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
<int-rsocket:outbound-gateway id="outboundGateway"
|
||||
client-rsocket-connector="clientRSocketConnector"
|
||||
auto-startup="false"
|
||||
command="fireAndForget"
|
||||
route-expression="'testRoute'"
|
||||
request-channel="requestChannel"
|
||||
publisher-element-type="byte[]"
|
||||
expected-response-type="java.util.Date"
|
||||
metadata-expression="{'metadata': new org.springframework.util.MimeType('*')}"/>
|
||||
----
|
||||
====
|
||||
|
||||
See `spring-integration-rsocket.xsd` for description for all those XML attributes.
|
||||
|
||||
[[rsocket-java-config]]
|
||||
=== Configuring RSocket Endpoints with Java
|
||||
|
||||
The following example shows how to configure an RSocket inbound endpoint with Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public RSocketInboundGateway rsocketInboundGatewayRequestReply() {
|
||||
RSocketInboundGateway rsocketInboundGateway = new RSocketInboundGateway("echo");
|
||||
rsocketInboundGateway.setRequestChannelName("requestReplyChannel");
|
||||
return rsocketInboundGateway;
|
||||
}
|
||||
|
||||
@Transformer(inputChannel = "requestReplyChannel")
|
||||
public Mono<String> echoTransformation(Flux<String> payload) {
|
||||
return payload.next().map(String::toUpperCase);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A `ClientRSocketConnector` or `ServerRSocketConnector` is assumed in this configuration with meaning for auto-detection of such an endpoint on the "`echo`" path.
|
||||
Pay attention to the `@Transformer` signature with its fully reactive processing of the RSocket requests and producing reactive replies.
|
||||
|
||||
The following example shows how to configure a RSocket inbound gateway with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow rsocketUpperCaseFlow() {
|
||||
return IntegrationFlows
|
||||
.from(RSockets.inboundGateway("/uppercase"))
|
||||
.<Flux<String>, Mono<String>>transform((flux) -> flux.next().map(String::toUpperCase))
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A `ClientRSocketConnector` or `ServerRSocketConnector` is assumed in this configuration with meaning for auto-detection of such an endpoint on the "`/uppercase`" path.
|
||||
|
||||
The following example shows how to configure a RSocket outbound gateway with Java:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "requestChannel", outputChannel = "replyChannel")
|
||||
public RSocketOutboundGateway rsocketOutboundGateway() {
|
||||
RSocketOutboundGateway rsocketOutboundGateway =
|
||||
new RSocketOutboundGateway(
|
||||
new FunctionExpression<Message<?>>((m) ->
|
||||
m.getHeaders().get("route_header")));
|
||||
rsocketOutboundGateway.setCommandExpression(
|
||||
new FunctionExpression<Message<?>>((m) -> m.getHeaders().get("rsocket_command")));
|
||||
rsocketOutboundGateway.setClientRSocketConnector(clientRSocketConnector());
|
||||
return rsocketOutboundGateway;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `setClientRSocketConnector()` is required only for the client side.
|
||||
On the server side, the `RSocketRequesterMethodArgumentResolver.RSOCKET_REQUESTER_HEADER` header with an `RSocketRequester` value must be supplied in the request message.
|
||||
|
||||
|
||||
The following example shows how to configure a RSocket outbound gateway with the Java DSL:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow rsocketUpperCaseRequestFlow(ClientRSocketConnector clientRSocketConnector) {
|
||||
return IntegrationFlows
|
||||
.from(Function.class)
|
||||
.handle(RSockets.outboundGateway("/uppercase")
|
||||
.command(RSocketOutboundGateway.Command.requestResponse)
|
||||
.expectedResponseType(String.class)
|
||||
.clientRSocketConnector(clientRSocketConnector))
|
||||
.get();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
See <<./dsl.adoc#java-dsl-gateway,`IntegrationFlow` as a Gateway>> for more information how to use a mentioned `Function` interface in the beginning of the flow above.
|
||||
@@ -160,7 +160,7 @@ See <<./http.adoc#http-outbound,HTTP Outbound Components>> for more possible con
|
||||
=== WebFlux Namespace Support
|
||||
|
||||
Spring Integration provides a `webflux` namespace and the corresponding schema definition.
|
||||
To include it in your configuration, include the following namespace declaration in your application context configuration file:
|
||||
To include it in your configuration, add the following namespace declaration in your application context configuration file:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
@@ -184,7 +184,7 @@ To include it in your configuration, include the following namespace declaration
|
||||
|
||||
==== Inbound
|
||||
|
||||
To configure Spring Integration WebFlux with XML, you caus use appropriate components from the `int-webflux` namespace: `inbound-channel-adapter` or `inbound-gateway`, corresponding to request and response requirements, respectively.
|
||||
To configure Spring Integration WebFlux with XML, you need to use appropriate components from the `int-webflux` namespace: `inbound-channel-adapter` or `inbound-gateway`, corresponding to request and response requirements, respectively.
|
||||
The following example shows how to configure both an inbound channel adapter and an inbound gateway:
|
||||
|
||||
====
|
||||
|
||||
@@ -25,6 +25,12 @@ See the https://github.com/spring-projects/spring-integration/wiki/Spring-Integr
|
||||
[[x5.2-new-components]]
|
||||
=== New Components
|
||||
|
||||
[[x5.2-rsocket-support]]
|
||||
==== RSocket Support
|
||||
|
||||
The `spring-integration-rsocket` module is now available with channel adapter implementations for RSocket protocol support.
|
||||
See <<./rsocket.adoc#rsocket,RSocket Support>> for more information.
|
||||
|
||||
[[x5.2-rate-limit-advice]]
|
||||
==== Rate Limit Advice Support
|
||||
|
||||
|
||||
Reference in New Issue
Block a user