For a fully non-blocking end to end reactive calls, is it recommended to explicitly call publishOn or subscribeOn to switch schedulers? For either cpu consuming or non consuming tasks, is it favorable to always use parallel flux to optimize performance?
For a fully non-blocking end to end reactive calls, is it recommended to explicitly call publishOn or subscribeOn to switch schedulers?
publishOn
is used when you publish data to downstream while subscribeOn
is used when you consume data from upstream. So it really depends on what kind of job you want to perform.
For either cpu consuming or non consuming tasks, is it favorable to always use parallel flux to optimize performance?
Absolutely not, Consider this example:
Flux.range(1, 10)
.parallel(4)
.runOn(Schedulers.parallel())
.sequential()
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
The above code is a total waste because i
will be processed almost instantly. Following code will perform better than above:
Flux.range(1, 10)
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
Now consider this:
public static <T> T someMethodThatBlocks(T i, int ms) {
try { Thread.sleep( ms ); }
catch (InterruptedException e) {}
return i;
}
// some method here
Flux.range(1, 10)
.parallel(4)
.runOn(Schedulers.parallel())
.map(i -> someMethodThatBlocks(i, 200))
.sequential()
.elapsed()
.subscribe(i -> System.out.printf(" %s ", i));
The output is similar to:
[210,3] [5,1] [0,2] [0,4] [196,6] [0,8] [0,5] [4,7] [196,10] [0,9]
As you can see that first response came in after 210
ms, followed by 3 responses with near 0
elapsed time in between. The cycle is repeated again and again. This is where you should be using parallel flux. Note that creating more number of threads doesn't guarantee performance because when there are more number of threads then context switching adds alot of overhead and hence the code should be tested well before deployment. If there are alot of blocking calls, having more than 1 number of thread per cpu may give you a performance boost but if the calls made are cpu intensive then having more than one thread per cpu will slow down the performance due to context switching.
So all in all, it always depends on what you want to achieve.
Worth stating I'm assuming the context here is Webflux rather than reactor in general (since the question is tagged as such.) The recommendations will can of course vary wildly if we're talking about a generalised reactor use case without considering Webflux.
For a fully non-blocking end to end reactive calls, is it recommended to explicitly call publishOn or subscribeOn to switch schedulers?
The general recommendation is not to explicitly call those methods unless you have a reason to do so. (There's nothing wrong with using them in the correct context, but doing so "just because" will likely bring no benefit.)
For either cpu consuming or non consuming tasks, is it favorable to always use parallel flux to optimize performance?
It depends what you're aiming to achieve, and what you mean by "CPU consuming" (or CPU intensive) tasks. Note that here I'm talking about genuinely CPU intensive tasks rather than blocking code - and in this case, I'd ideally farm the CPU intensive part out to another microservice, enabling you to scale that as required, separately from your Webflux service.
Using a parallel flux (and running it on the parallel scheduler) should use all available cores to process data - which may well result in it being processed faster. But bear in mind you also have, by default, an event loop running for each core, so you've essentially "stolen" some available capacity from the event loop in order to achieve that. Whether that is ideal depends on your use-case, but usually it won't bring much benefit.
Instead, there's two approaches I'd recommend:
- If you can break your CPU intensive task down into small, low intensity chunks, do so - and then you can keep it on the event loop. This allows the event loop to keep running in a timely fashion while these CPU intensive tasks are scheduled as they're able to be.
- If you can't break it down, spin up a separate Scheduler (optionally with a low priority so it's less likely to steal resources from the event loop), and then farm all your CPU intensive tasks out to that. This has the disadvantage of creating a bunch more threads, but again keeps the event loop free. By default you'll have as many threads as there are cores for the event loop - you may wish to reduce that in order to give your "CPU intensive" scheduler more cores to play with.
© 2022 - 2024 — McMap. All rights reserved.