2

I have an application which connects to third party service and gets the result back to client. Inside , app make an GET request to third party service and gets the result. I have used Reactor and reactive code to scale the application at heavy loads. This is a Spring Boot project which runs embedded Tomcat and relies on Web Client( Reactive netty for making request to third party). Somehow , CPU Utilization and response times are worse than blocking mode. The hardware setup has single core running in Kubernetes.

The project is set up inside Kubernetes and running on single pod with one core. I tried in reactive way , but the app is much slower and even CPU Utilization is high when compared to blocking architecture.

 public Mono<ResponseEntity<?>> get(HttpServletRequest request)
   {
      return Mono.create ( callback -> {
         Mono<Response> response = Make HTTP GET Call using webClient.
         response.subscribe(response -> {
         callback.success(response);
         },error -> {
         callback.error(error);
         }
       });
    }

With Traditional Blocking mode , I am ble to see better performance and decreased CPU usage when comapred to reactive approach. What could be the reason for this disparity ? Is it because of context switching as there is only one core ? If yes , how do we attain better performance through single core architecture ?

Akash Sateesh
  • 712
  • 2
  • 7
  • 16
  • what is the reason for the internal subscription? subscribe is an expensive process and should only be done by the client once. – Toerktumlare Jun 23 '19 at 12:55
  • @ThomasAndolf I wanted to get the results of Web Client , so I am subscribing again. Is there any other way to do it ? – Akash Sateesh Jun 24 '19 at 05:15

2 Answers2

2

i don't really understand why you subscribe in the middle to extract the response. Subscribing i think is an expensive process and should only be done once by the calling client.

This is an example of how i would do it using the doOnError method to handle errors and mapping the response.

public Mono<Foo> bar() {
    return client.get()
            .uri("/something")
            .accept(MediaType.APPLICATION_JSON)
            .exchange()
            .flatMap(response -> response.bodyToMono(Foo.class))

}

public Mono<Bar> foo() {
    return bar()
        .flatMap(stuff -> {
         return // Map it here to something else that 
                // is getting returned to the calling client
        })
        .doOnError(FooBarException::new);
Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • Thanks, Thomas for the insights. Can you please let me know why subscribing is an expensive process? I didn't really get that part – Akash Sateesh Jun 23 '19 at 14:07
  • Mono.create should be lazy, so I don't think it's implying any eager subscription. But it's definitely way more complicated to reason about than your suggested code. https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#create-java.util.function.Consumer- – Yuri Schimke Jun 23 '19 at 14:55
  • "nothing happens until you subscribe" which means this pice of code will be called even if there is an error in the chain even though there might be a fault higher up in the chain. As said, it should be lazy and the call should be made only when needed. – Toerktumlare Jun 23 '19 at 18:22
  • To get the result of Web Client , we need to subscribe right ? Is there any other way to get it ? – Akash Sateesh Jun 24 '19 at 05:10
  • in general we are not the subscribers. The client calling your service is the subscriber. If you need the response to process it depending on status code, you flatmap the response and check the status code in the flatmap, and depending on what status you either return a mono or a mono error. Then later in the chain you act upon that mono error in a `doOnError` – Toerktumlare Jun 24 '19 at 11:58
  • if you look in my code, in my flatmap i call `response.bodyToMono` this is just a helper class to deserialize a response body into a class that is wrapped by a mono. You can without any problem check a statusCode here and then do what you want. What is important is that you need to return a `Mono` it cold be an error mono or a regular one. it needs to be a mono. – Toerktumlare Jun 24 '19 at 12:02
  • WebClient and Mono are both monads, just like optional and stream, if you have no idea how to work with them or what they do you can check out my article about how to write your own it explains how they work under the hood. https://medium.com/swlh/write-a-monad-in-java-seriously-50a9047c9839 – Toerktumlare Jun 24 '19 at 12:05
  • 1
    and when we talk about something being eager or deferred you can read about it here https://stackoverflow.com/questions/55955567/what-does-mono-defer-do – Toerktumlare Jun 24 '19 at 13:33
  • subscribing in the middle is expensive because it is blocking. – Toerktumlare Jun 29 '19 at 19:18
1

There should be just one subscription in your entire reactive chain. Delay it as much as possible - one subscription at the end. Let us re-write your code:

public Mono<ResponseEntity<?>> get(HttpServletRequest request) {
      return Mono.just(requestObject)
                 .flatmap(a -> makeAsyncCall(a))
                 .doOnError(err -> doSomethingOnError());
}

You can subscribe where are you starting your reactive chain. Typically, this would be the controller.

This would be:

Mono.just(request)
    .flatMap(request -> get(request))
    .subscribe(response -> setDeferredResult(response));

return deferredResult;
Prashant Pandey
  • 4,332
  • 3
  • 26
  • 44
  • 1
    nit: In most cases you hope a Reactive aware framework is calling your code and happy getting a Mono back that it knows what to do with. subscribe returns a Disposable so this example is throwing away the response. – Yuri Schimke Jun 25 '19 at 05:41
  • You're right. You can enrich a deferredResult in the subscribe and return it instread. I have updated my answer to reflect this. – Prashant Pandey Jun 25 '19 at 05:48