Chained reactive components invocation - java

I have a list of following objects with method returning reactive type Mono<?>:
interface GuyWithReactiveReturnTypeMethod {
Mono<String> execute();
}
class ReactiveGuysInvocator {
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToInvoke) {
???
}
}
And I need to invoke all the guys one by one (n's guy result is n+1's guy argument), but I'm not sure how can I iterate over such list.
I thought of flatMaping next guy in a while loop:
public interface GuyWithReactiveReturnTypeMethod {
Mono<String> execute(String string);
}
class ReactiveGuysInvocator {
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToExecute) {
ListIterator<GuyWithReactiveReturnTypeMethod> iterator = guysToExecute.listIterator();
Mono<String> currentResult = Mono.just("start");
while (iterator.hasNext()) {
GuyWithReactiveReturnTypeMethod guyToInvoke = iterator.next();
currentResult = currentResult.flatMap(guyToInvoke::execute)
.doOnNext(object -> System.out.println("Executed!"))
.doOnError(error -> System.out.println("Error"));
}
return currentResult;
}
}
But this approach seems to be completely incorrect.
Does anyone know how could I implement something like this?

UPDATE: flatMap can be easily abused. Make sure that you are doing asynchronous work when using flatMap. Mostly, it seems to me, that you can do pretty well with a minimum of Mono.just.
Flatmap is what you have to do with the constraints you provide.
executeAllGuys(Arrays.asList(new GuyWithReactiveReturnTypeMethod[] {
(s)->Mono.just(s+"1"),
(s)->Mono.just(s+"2"),
(s)->Mono.just(s+"3")}))
.subscribe(System.out::println);
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToExecute) {
// your flow is starting here
Mono<String> stringMono = Mono.just("start");
for ( GuyWithReactiveReturnTypeMethod guyToInvoke: guysToExecute) {
stringMono = stringMono.flatMap(guyToInvoke::execute);
}
return stringMono;
}
Just look at all those Mono.just calls. Why do you want to create N+1 flows to do the job? The real problem is you're creating a new flow every time you execute the interface method. Flatmap stops the current flow and starts a new one with the publisher returned by the flatMap method. Try to think reactive and treat the whole business like a stream. There is no flatMap in Streams. A reactive execution should be done on only a single flow.
A Mono<String> execute(String string) is not a reactive component. It is a reactive producer. A Mono<String> execute(Mono<String> string) is a reactive component.
Make your interface more reactive by taking a Mono in and returning a Mono. Your application is doing a map conversion on at each step. This is "chaining reactive components".
executeAllGuys(Arrays.asList(new GuyWithReactiveReturnTypeMethod[] {
(s)->s.map(str->str+"1"),
(s)->s.map(str->str+"2"),
(s)->s.map(str->str+"3")}))
.subscribe(System.out::println);
Mono executeAllGuys(List guysToExecute) {
// your flow is starting here
Mono stringMono = Mono.just("start");
for ( GuyWithReactiveReturnTypeMethod guyToInvoke: guysToExecute) {
stringMono = guyToInvoke.execute(stringMono);
}
return stringMono;
}
interface GuyWithReactiveReturnTypeMethod {
Mono execute(Mono string);
}
Make your interface less reactive but make your application more reactive by using a Flux instead of a list. You will then have to use reduce to convert a Flux to a Mono. Your application is doing a Map/Reduce function. I don't think a Flux will guarantee execution order of the elements in the flow but it could executeAllGuys more efficiently.
// your flow is starting here
executeAllGuys(Flux.just(
(s)->s+"1",
(s)->s+"2",
(s)->s+"3"))
.subscribe(System.out::println);
Mono executeAllGuys(Flux guysToExecute) {
return guysToExecute.reduce("start", (str, guyToInvoke)->guyToInvoke.execute(str));
}
interface GuyWithReactiveReturnTypeMethod {
String execute(String string);
}
Reference: Reactive Programming: Spring WebFlux: How to build a chain of micro-service calls?

Related

Call Mono after first Mono is done and return result of first Mono

I want to perform two business operations, in a Webflux environment, in a way that the second operation happens only after the first one is succesfull. After the second one is done, I want to return the result of the first operation. The second operation calls a org.springframework.web.reactive.function.client.WebClient. This is what I have tried until now:
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
service.operation2();
return resource;
})
.map(ResponseEntity::ok);
}
I also tried with then and subscribe but I can't get the webclient to perform the call and return the result of service.operation1. What must I do?
You need to construct a flow using reactive operators and let WebFlux subscribe to it. In your snippet there is no subscription to service.operation2()
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
return service.operation2()
.thenReturn(resource);
})
...
}

Is it possible to create a dynamic filter/predicate in Mono/Flux?

The app is a simple processing - reading data, filtering it, process and write filtered data.
Here is a simple code which runs without any issue:
void run() {
Flux.interval(Duration.ofMillis(200))
.filter(value -> getPredicate().test(value))
.flatMap(this::writeData)
.subscribe();
}
private Predicate<Long> getPredicate() {
return value -> value % 2 == 0;
}
Is it possible to have dynamic predicate which will be retrieved from remote web service with periodic requests?
If possible - how to use Mono<Predicate> inside .filter() and keep it non-blocking
For example replacing getPredicate() with below:
private Mono<Predicate<Long>> getPredicateFromRemoteServer() {
return webClient.get()
.uri("/filters/1")
.retrieve()
.bodyToMono(Filter.class)
.map(this::mapToPredicate)
.cache(v -> Duration.ofMinutes(10), ex -> Duration.ZERO, () -> Duration.ZERO);
}
private Predicate<Long> mapToPredicate(Filter filter) {
// here will be converting filter object into predicate
return value -> value > 5;
}
Ideally I would like to avoid cache(Duration.ofMinutes(10)) because filter could be updated each minute, or each day... and once filter is updated my service get notified, but I didn't find a way to invalidate cache externally, that's why Duration.ofMinutes(10) is used for some approximate invalidation.
Well, perhaps you could write the pipeline a bit differently. Instead of aspiring to return a new Predicate every time your process an item in your stream by calling getPredicateFromRemoteServer(), you could make the function itself your predicate. Pass the value you are processing from the stream and make it return a Mono<Boolean> with the answer and use that in a filterWhen pipe in your pipeline.
For example, somewhat like this:
private Mono<Boolean> isWithinThreshold(int value) {
return webClient.get()
.uri("/filters/1")
.retrieve()
.bodyToMono(Filter.class)
.map(filter -> filter.threshold <= value)
.cache(v -> Duration.ofMinutes(10), ex -> Duration.ZERO, () -> Duration.ZERO);
}
Then in your main pipeline you can do:
Flux.interval(Duration.ofMillis(200))
.filterWhen(value -> isWithinThreshold(value))
.flatMap(this::writeData)
.subscribe();
}

Do then and finally with Flowable reactive x Java

Trying to use Flowable, do then, and finally using RxJava3.
public String post(Publisher<CompletedFileUpload> files) {
return Flowable.fromPublisher(files).doOnNext(file -> {
MultipartBody requestBody = MultipartBody.builder()
.addPart("file", file.getFilename(), MediaType.MULTIPART_FORM_DATA_TYPE, file.getBytes())
.addPart("id", "asdasdsds")
.build();
}).doOnComplete((value) -> {
return this.iProduct.post(requestBody);
});
}
The above code has error, But what I am trying to achieve is described in the below scenarios
Iterate on files
add file.getFilename() and bytes to requestBody
Then call the this.iProduct.post(requestBody) which returns the string
Finally return the string value
One way to approach this is to:
Gather all emissions that would come out of Publisher<CompletedFileUpload> files with the toList() operator
Construct the request by looping through the list created in in Step 1 using the map() operator.
Post the request and return the resulting String (also using the map() operator.
The scaffolding for this would look something like this:
public String post(Publisher<CompletedFileUpload> files) {
final Single<MultipartBody> requestSingle =
Flowable.fromPublisher(files)
.toList()
.map(list -> {
final MultipartBody.Builder builder = MultipartBody.Builder();
for(file : list) {
builder.addPart(...)
}
return builder.build();
})
.map(requestBody -> this.iProduct.post(requestBody));
return requestSingle.blockingGet();
}
There are two things worth noting here:
The toList() operator transforms the Flowable into a Single.
Your sample mixes asynchronous code (all the Rx stuff) and synchronous code (the post method returns a String as opposed to a deferred operation/value). The Rx operators are helpful ways of transforming from one reactive type to another, but in your case you need a way to bridge into the synchronous world by invoking those asynchronous operations and waiting for the resulting value. This is the reason for the final call to blockingGet().

How to call Synchronus blocking method inside async method in Java?

I'm Using Project Reactor library. Here is my scenario.
I want to call the blocking service inside my non blocking method.
I have a three different services, I called those three services from my springboot application. Here is my sample code
public Mono<Example> getValuesFromDifferentServices() {
Mono<Example1> mono1=service.getService1();
Mono<Example2> mono2=service.getService2();
mono1.zipwith(mono2)
.map(value-> {
// some logics then
if(value.getT1().getStatus().equals(value.getT2().getStatus())) {
Mono<Example3> mono3 = service.getService3(true);
mono3.map(f-> {
value.getT1().setSomething(f.getSomething);
return f;
}).subscribe();
}
return value.getT1();
})
}
Note: Above example is not the actual logic. But the implementation is similar to that
Even I tried to subscribe() it, I couldn't get the 3rd service value all the time (uncertainty values). I cannot block() the 3rd service since it is not allowed. How to achieve this?
Update: 3rd Service input would be decided after If condition either it should be true or not Mono<Example3> mono3 = service.getService3(true);
We should call the 3rd service if only the condition matches, otherwise calling the 3rd service is not required and which is not advisable., If condition doesn't match, we should not invoke 3rd service.
This example is a little wierd but as I understand, you want to call the first two services, each give you back a single value.
After that you want to call the third one if necessary and set a value from this into one of the first's field.
Anyway, there is a simple solution, but with more information maybe we can create nicer stream. This stream takes adventages of flatMap, which eagerly subscribes into the inner publisher.
[The example was written in Kotlin, it's very like Java. The only confusig thing here maybe the it variable, which is equals something like this: map(it -> it.sg )]
data class Example(
val name: String,
val status: String,
var value: String? = null
)
class ReactorTest {
#Test
fun test() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "suspended"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended", "thirdValue"))
.verifyComplete()
}
#Test
fun test2() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "active"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended"))
.verifyComplete()
}
}
Side note: if you're using blocking services in your reactive streams, those should be separated into dedicated threadpools. Like:
fun blockingService(): Mono<String> {
//real service use fromCallable
return Mono.just("fromCallableOnServiceCall")
//for real service it may use a dedicated pool
.subscribeOn(Schedulers.boundedElastic())
}

Convert traditional loop of invoking weclient into non blocking way

I am new to reactive programming and I want to transform the following code into non blocking way.
For the sake of simplicity, I created a sample pseudo code based from my original code. Any help will be appreciated.
public Mono<Response> getResponse(List<Provider> providers) {
for (Provider provider : providers) {
Response response = provider.invokeHttpCall().block();
if(response.getMessage() == "Success") {
return Mono.just(response);
}
continue;
}
return Mono.empty();
}
provider.invokeHttpCall() method
#Override
public Mono<Response> invokeHttpCall(){
WebClient webClient = WebClient.create();
return webClient.post()
.uri("/provider").accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Response.class);
}
I tried several tactics to implement this, but still no luck. Either all providers are invoked or I need to block the webclient thread.
Flux.fromIterable(providers)
.concatMap(Provider::invokeHttpCall) // ensures providers are called sequentially
.filter(response -> response.getMessage().equals("Success"))
.next()
reactive is a kind of Stream. Please think it as a Stream and program it reactively.
I give you such followed code.
Firstly, use Flux.fromIterable() to create a flux stream from a List.
Next, use flatmap() and Lambda fuction to emit the invoke into another new thread.
use method filterWhen() and Lambda to get the "Success" response and just get the first "Success" elements. See filterwhen api Doc.
Finally, just use Mono.from() to wrap the Flux and then return the Mono type.
public Mono<Response> getResponse(List<Provider> providers) {
return Mono.from(Flux.fromIterable(providers)
.flatmap(provider ->
Mono.defer(() -> provider.invokeHttpCall())
.filterWhen(response -> response.getMessage() == "Success");
}
if you want to see result and println().
Just use .subsribe() method to excute it.
getResponse.subsribe(System.out::println);

Categories

Resources