INT-4300: Add WebFlux Server Support
JIRA: https://jira.spring.io/browse/INT-4300 * Add `ReactiveHttpInboundEndpoint` based on the WebFlux foundation * Extract `BaseHttpInboundEndpoint` for common HTTP Inbound Channel Adapters options * Make `spring-webmvc` and `spring-webflux` as `optional` dependencies to let end-user to choose * Refactor `HttpContextUtils` to include constants for newly added WebFlux support * Introduce `BaseHttpInboundEndpoint.setRequestPayloadTypeClass()` for raw `Class<?>` and modify existing `setRequestPayloadType()` for the `ResolvableType` * Refactor existing MVC tests and XML components parsers to use new `setRequestPayloadTypeClass()` * Add `MessagingGatewaySupport.sendAndReceiveMessageReactive()` to get a reply from downstream flow reactive back-pressure manner * Add `IntegrationHandlerResultHandler` implementation to let WebFlux infrastructure to handle the `Mono<Void>` from the `ReactiveHttpInboundEndpoint` properly * Fix `JdbcLockRegistryLeaderInitiatorTests` race condition to assert the `initiator1` is elected eventually after yielding when the `initiator2` is stopped * Fix JavaDocs issue in the `HttpRequestHandlingMessagingGateway` * Move all the "hard" logic in the `MessagingGatewaySupport#doSendAndReceiveMessageReactive` to the `Mono` chain ensuring back-pressure when `sendAndReceiveMessageReactive()` is called not from the Reactive Stream Add test-case to demonstrate SSE JIRA: https://jira.spring.io/browse/INT-3625 Some polishing and optimization for the `MessagingGatewaySupport.doSendAndReceiveMessageReactive()` More optimization for `MessagingGatewaySupport` * Upgrade to Reactor 3.1 M3 * Document WebFlux-based components Minor Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
e475d9c695
commit
d4a99919ed
@@ -122,6 +122,59 @@ This also shows how to customize the HTTP methods accepted by the gateway, which
|
||||
The reply message will be available in the Model map.
|
||||
The key that is used for that map entry by default is 'reply', but this can be overridden by setting the 'replyKey' property on the endpoint's configuration.
|
||||
|
||||
=== WebFlux Server Side support
|
||||
|
||||
Starting with _version 5.0_, the `ReactiveHttpInboundEndpoint`, http://docs.spring.io/spring/docs/5.0.0.RC3/spring-framework-reference/web.html#web-reactive[WebFlux] `WebHandler`, implementation is provided.
|
||||
This component is similar to the MVC-based `HttpRequestHandlingEndpointSupport` with which it shares some common options via the newly extracted `BaseHttpInboundEndpoint`.
|
||||
Instead of MVC, it is used in the Spring WebFlux Reactive environment.
|
||||
A simple sample for explanation:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableIntegration
|
||||
public class ReactiveHttpConfiguration {
|
||||
|
||||
@Bean
|
||||
public ReactiveHttpInboundEndpoint simpleInboundEndpoint() {
|
||||
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
|
||||
RequestMapping requestMapping = new RequestMapping();
|
||||
requestMapping.setPathPatterns("/test");
|
||||
endpoint.setRequestMapping(requestMapping);
|
||||
endpoint.setRequestChannelName("serviceChannel");
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel = "serviceChannel")
|
||||
String service() {
|
||||
return "It works!";
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
As can be seen, the configuration is similar to the `HttpRequestHandlingEndpointSupport` mentioned above, except that we use `@EnableWebFlux` to add the WebFlux infrastructure to our integration application.
|
||||
Also, the `ReactiveHttpInboundEndpoint` performs `sendAndReceive` operation to the downstream flow using back-pressure, on demand based capabilities, provided by the reactive HTTP server implementation.
|
||||
|
||||
NOTE: The reply part is non-blocking as well and based on the internal `FutureReplyChannel` which is flat-mapped to a reply `Mono` for on demand resolution.
|
||||
|
||||
The `ReactiveHttpInboundEndpoint` can be configured with a custom `ServerCodecConfigurer`, `RequestedContentTypeResolver` and even a `ReactiveAdapterRegistry`.
|
||||
The latter provides a mechanism where we can return a reply as any reactive type - Reactor `Flux`, RxJava `Observable`, `Flowable` etc.
|
||||
This way, we can simply implement https://en.wikipedia.org/wiki/Server-sent_events[Server Sent Events] scenarios with Spring Integration components:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow sseFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Http.inboundReactiveGateway("/sse")
|
||||
.requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
|
||||
.handle((p, h) -> Flux.just("foo", "bar", "baz"))
|
||||
.get();
|
||||
}
|
||||
----
|
||||
|
||||
[[http-outbound]]
|
||||
=== Http Outbound Components
|
||||
==== HttpRequestExecutingMessageHandler
|
||||
@@ -193,8 +246,8 @@ Of course, this can be an abstract class, or even an interface (such as `java.io
|
||||
|
||||
==== ReactiveHttpRequestExecutingMessageHandler
|
||||
|
||||
The `ReactiveHttpRequestExecutingMessageHandler` implementation is very similar to `HttpRequestExecutingMessageHandler` instead of delegating to a `WebClient` from Spring Framework WebFlux module.
|
||||
To configure it, write a bean like this:
|
||||
The `ReactiveHttpRequestExecutingMessageHandler` (starting with _version 5.0_) implementation is very similar to `HttpRequestExecutingMessageHandler`, using a `WebClient` from the Spring Framework WebFlux module.
|
||||
To configure it, define a bean like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
@@ -686,6 +739,26 @@ public RequestMapping mapping() {
|
||||
requestMapping.setMethods(HttpMethod.POST);
|
||||
return requestMapping;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveHttpInboundEndpoint jsonInboundEndpoint() {
|
||||
ReactiveHttpInboundEndpoint endpoint = new ReactiveHttpInboundEndpoint();
|
||||
RequestMapping requestMapping = new RequestMapping();
|
||||
requestMapping.setPathPatterns("/persons");
|
||||
endpoint.setRequestMapping(requestMapping);
|
||||
endpoint.setRequestChannel(fluxResultChannel());
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel fluxResultChannel() {
|
||||
return new FluxMessageChannel();
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel = "fluxResultChannel")
|
||||
Flux<Person> getPersons() {
|
||||
return Flux.just(new Person("Jane"), new Person("Jason"), new Person("John"));
|
||||
}
|
||||
----
|
||||
|
||||
.Inbound Gateway Using the Java DSL
|
||||
@@ -699,6 +772,17 @@ public IntegrationFlow inbound() {
|
||||
.channel("httpRequest")
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow httpReactiveInboundChannelAdapterFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Http.inboundReactiveChannelAdapter("/reactivePost")
|
||||
.requestMapping(m -> m.methods(HttpMethod.POST))
|
||||
.requestPayloadType(ResolvableType.forClassWithGenerics(Flux.class, String.class))
|
||||
.statusCodeFunction(m -> HttpStatus.ACCEPTED))
|
||||
.channel(c -> c.queue("storeChannel"))
|
||||
.get();
|
||||
}
|
||||
----
|
||||
|
||||
.Outbound Gateway Using Java Configuration
|
||||
@@ -713,6 +797,16 @@ public HttpRequestExecutingMessageHandler outbound() {
|
||||
handler.setExpectedResponseType(String.class);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel = "reactiveHttpOutRequest")
|
||||
@Bean
|
||||
public ReactiveHttpRequestExecutingMessageHandler reactiveOutbound(WebClient client) {
|
||||
ReactiveHttpRequestExecutingMessageHandler handler =
|
||||
new ReactiveHttpRequestExecutingMessageHandler("http://localhost:8080/foo", client);
|
||||
handler.setHttpMethod(HttpMethod.POST);
|
||||
handler.setExpectedResponseType(String.class);
|
||||
return handler;
|
||||
}
|
||||
----
|
||||
|
||||
.Outbound Gateway Using the Java DSL
|
||||
@@ -726,6 +820,18 @@ public IntegrationFlow outbound() {
|
||||
.expectedResponseType(String.class))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow outboundReactive() {
|
||||
return f -> f
|
||||
.handle(Http.<MultiValueMap<String, String>>outboundReactiveGateway(m ->
|
||||
UriComponentsBuilder.fromUriString("http://localhost:8080/foo")
|
||||
.queryParams(m.getPayload())
|
||||
.build()
|
||||
.toUri())
|
||||
.httpMethod(HttpMethod.GET)
|
||||
.expectedResponseType(String.class));
|
||||
}
|
||||
----
|
||||
|
||||
[[http-timeout]]
|
||||
|
||||
@@ -29,11 +29,11 @@ The new `MongoDbOutboundGateway` allows you to make queries to the database on d
|
||||
|
||||
See <<mongodb-outbound-gateway>> for more information.
|
||||
|
||||
==== HTTP Reactive Outbound Gateway and Channel Adapter
|
||||
==== HTTP Reactive Inbound and Outbound Gateways and Channel Adapters
|
||||
|
||||
The new `ReactiveHttpRequestExecutingMessageHandler` adds support for WebFlux `WebClient` for outbound channel adapter and gateway.
|
||||
The new `ReactiveHttpInboundEndpoint` and `ReactiveHttpRequestExecutingMessageHandler` add support for Spring WebFlux Framework gateways and channel adapters.
|
||||
|
||||
See <<http-outbound>> for more information.
|
||||
See <<http>> for more information.
|
||||
|
||||
==== Content Type Conversion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user