1

Please look at the below code from controller(Added comments) which uses RestTemplate:

@GetMapping("/{courseid}")
public Course getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Price price = restTemplate.getForObject("http://localhost:8002/price/"+courseid, Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Enrollment enrollment = restTemplate.getForObject("http://localhost:8003/enrollment/"+courseid, Enrollment.class);
    
    //Consolidate everything in to Course object and send it as response
    return new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), price.getDiscountedPrice(),
            enrollment.getEnrollmentOpen());
}

Now I am trying to achieve the same using Reactive programming. I now use WebClient and Mono from Web-Flux. But, I am so confused as to how to combine the results? Take a look at the below code (Just using Mono Everywhere. Rest of the code remained same)

@GetMapping("/{courseid}")
public Mono<Course> getCourseDetails(@PathVariable Long courseid) {

    // Get Course info (ID, Name, Description) from pre-populated Array List
    CourseInfo courseInfo = getCourseInfo(courseid);
    
    // Get Price info of a course from another microservice using RESTTemplate
    Mono<Price> price = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
    
    // Get enrollment info of a course from another microservice using RESTTemplate
    Mono<Enrollment> inventory = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
    
    //Question : How do we Consolidate everything and form a Mono<Course> object and send it as response?
    
}

Question 1 : How do we Consolidate everything and form a Mono object and send it as response?

Question 2 : Does the statement "CourseInfo courseInfo = getCourseInfo(courseid);" cause blocking operation?

Thanks!

rakesh mehra
  • 618
  • 1
  • 9
  • 21

2 Answers2

1
  1. restTemplate.getForObject returns simple object - in your case Price or Enrollment. To convert them to Mono you can simply Mono.just(object), however better solution would be to switch to Webclient which is default HTTP client for Spring Reactive

  2. getCourseInfo it depends what is the logic behind this method. For sure if there is a JDBC connection behind that method it is blocking.

  3. To make a final response with Mono<Course> you should think about zip operator which will help you with that.

For ex:

Mono<Course> courseMono = Mono.zip(price, enrollment)
        .map(tuple -> new Course(courseInfo, tuple.getT1(), tuple.getT2()));
mslowiak
  • 1,688
  • 12
  • 25
1

Answering to:

Question 1 : How do we Consolidate everything and form a Mono object and send it as response?

Mono.zip(..) is what you need to combine the two results. This diagram is from the doc :

zip doc

Note that, zip will result in an empty Mono if one of A or 1 is empty! Use switchIfEmpty/defaultIfEmpty to protect against that case.

Thus the code looks like:

@GetMapping("/{courseid}")
public Mono<Course> getCourseDetails(@PathVariable Long courseid) {

    CourseInfo courseInfo = getCourseInfo(courseid);
    Mono<Price> priceMono = webClient.get().uri("http://localhost:8002/price/{courseid}/",courseid).retrieve().bodyToMono(Price.class);
    Mono<Enrollment> enrollmentMono = webClient.get().uri("http://localhost:8003/enrollment/{courseid}/",courseid).retrieve().bodyToMono(Enrollment.class);
    
    return Mono.zip(priceMono, enrollmentMono).map(t -> new Course(courseInfo.getCourseID(), courseInfo.getCourseName(), courseInfo.getCourseDesc(), t.getT1().getDiscountedPrice(),
            t.getT2().getEnrollmentOpen()));
    
}

Now answering to:

Question 2 : Does the statement "CourseInfo courseInfo = getCourseInfo(courseid);" cause blocking operation?

Since you mentioned that Get Course info (ID, Name, Description) from pre-populated Array List, if it is just an in-memory Array containing the course information, then it's not blocking.

But (as @mslowiak also mentioned), if getCourseInfo contains logic which involves querying a database, ensure that you are not using a blocking JDBC driver. If so, then there is no point of using Webflux and Reactor. Use Spring R2DBC if that is the case.

Abhinaba Chakraborty
  • 3,488
  • 2
  • 16
  • 37
  • Thank you! That answers. Also, do you know how can we be sure, that there are no blocking operations, while using reactive programming? – rakesh mehra Jul 26 '20 at 18:35
  • @rakeshmehra well, there is no such fixed rule. You can google up for that. or find it intuitively. eg You can check that the underlying implementation of the JDBC driver is a blocking one because it has no reference of Flux/Mono anywhere in it's classes. i.e it is not using Reactor. The only 2 reactive projects in Java are RxJava and Reactor – Abhinaba Chakraborty Jul 26 '20 at 19:14
  • In large project with hundreds of microbreweries, even if you have one blocking operation somewhere, it might defeat the purpose of reactive programming. I believe it's difficult to find blocking operations manually. But, there seem to be a Library that detects blocking operations. – rakesh mehra Jul 27 '20 at 06:50
  • First of all, you cant call a blocking method when using Spring Webflux. It throws error. You can try it out. So it wont work with the old jdbc driver too. I donno if you mean microservices , when you said microbreweries. When a call is going to a different microservice, you dont have to worry about that being reactive or not. – Abhinaba Chakraborty Jul 27 '20 at 09:03
  • Yes, Accepted as solution. – rakesh mehra Aug 01 '20 at 10:04