Depending on how many cores we have for your micro-services, we may have as low as 4 threads (reactor-http-nio- or reactor-http-epoll-) when it comes to listening and talking to outside. If these threads are blocked in a way we didn't expect, the performance could be worse than traditional synchronised paradigm. Here're a few examples to show how it works when offloading blocking operations to a larger thread pool at different places. Logs happen at doOnSubscribe and doOnSuccess.
no scheduler
[ctor-http-nio-4] Controller : Received request
[ctor-http-nio-4] ServiceClient : Getting something
[ctor-http-nio-4] ServiceClient : Got something in 116ms
[ctor-http-nio-4] Controller : Processed in 120ms
scheduler on subscribeOn WebClient call to 3rd party
[ctor-http-nio-3] Controller : Received request
[ scheduler-3] ServiceClient : Getting something
[ctor-http-nio-4] ServiceClient : Got something in 116ms
[ctor-http-nio-4] Controller : Processed in 120ms
scheduler on publishOn WebClient call to 3rd party
[ctor-http-nio-5] Controller : Received request
[ctor-http-nio-5] ServiceClient : Getting something
[ scheduler-12] ServiceClient : Got something in 116ms
[ scheduler-12] Controller : Processed in 120ms
scheduler on subscribeOn and publishOn WebClient call to 3rd party
[ctor-http-nio-3] Controller : Received request
[ scheduler-3] ServiceClient : Getting something
[ scheduler-4] ServiceClient : Got something in 116ms
[ scheduler-4] Controller : Processed in 120ms
Bonus cases to make things even tricky.
subscribeOn after doOnSubscribe in controller
[undedElastic-21] Controller : Received request
[undedElastic-21] ServiceClient : Getting something
[ctor-http-nio-7] ServiceClient : Got something in 116ms
[ctor-http-nio-7] Controller : Processed in 120ms
subscribeOn before doOnSubscribe in controller
[ctor-http-nio-3] Controller : Received request
[undedElastic-21] ServiceClient : Getting something
[ctor-http-nio-4] ServiceClient : Got something in 116ms
[ctor-http-nio-4] Controller : Processed in 120ms