Spring MongoDB Functional Reactive Microservices Example

Microservice – Currency Conversion

Creating Project

Create a gradle based project in Eclipse, the project name is spring-boot-reactive-currency-conversion-microservice.

Updating Build Script

Update build.gradle script to include the required dependencies for downloading required jars for our application.

buildscript {
    ext {
        springBootVersion = '2.1.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
plugins {
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux:${springBootVersion}")
    implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
}
dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE'
    }
}
Creating VO Class

We need to create a dto or vo class that will be sent as a response object to the end users or clients.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.vo;
public class CurrencyExchange {
	private String id;
	private String fromCur;
	private String toCur;
	private Double rateCur;
	private Integer quantity;
	private Double totalAmount;
	public CurrencyExchange() {
	}
	public CurrencyExchange(String id, String fromCur, String toCur, Double rateCur, Integer quantity,
			Double totalAmount) {
		this.id = id;
		this.fromCur = fromCur;
		this.toCur = toCur;
		this.rateCur = rateCur;
		this.quantity = quantity;
		this.totalAmount = totalAmount;
	}
	//getters and setters
}
Creating a Feign Proxy

We can call REST service using RestTemplate but we have to write lots of code to make a simple call. Therefore it is better to create a feign proxy to call a REST service.

In the below code, @FeignClient(name = “forex-microservice”, url = “localhost:8080”) tells that this is a Feign Client and the url at which forex-service is present is localhost:9000.

@GetMapping(“forex/fromCur/{fromCur}/toCur/{toCur}”) indicates the URI of the service we want to consume.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.proxy;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.roytuts.spring.boot.reactive.currency.conversion.microservice.vo.CurrencyExchange;
@FeignClient(name = "forex-microservice", url = "localhost:8080")
public interface CurrencyConversionProxy {
	@GetMapping("/forex/fromCur/{fromCur}/toCur/{toCur}")
	public CurrencyExchange getCurrency(@PathVariable(name = "fromCur") String fromCur,
			@PathVariable(name = "toCur") String toCur);
}
Creating Handler Functions

Handler function is one of the two important functions of Reactive Streams – Handler Function and Router Function.

To know more on Handler Function you may read here and here.

Notice in the below class how we are converting Mono entity object to Mono VO object.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.roytuts.spring.boot.reactive.currency.conversion.microservice.proxy.CurrencyConversionProxy;
import com.roytuts.spring.boot.reactive.currency.conversion.microservice.vo.CurrencyExchange;
import reactor.core.publisher.Mono;
@Component
public class CurrencyConversionHandler {
	@Autowired
	private CurrencyConversionProxy proxy;
	public Mono<ServerResponse> getCurrencyExchange(ServerRequest request) {
		String fromCur = request.pathVariable("from");
		String toCur = request.pathVariable("to");
		Integer quantity = Integer.parseInt(request.pathVariable("quantity"));
		CurrencyExchange currencyExchange = proxy.getCurrency(fromCur, toCur);
		Mono<CurrencyExchange> mono = Mono.just(new CurrencyExchange(currencyExchange.getId(),
				currencyExchange.getFromCur(), currencyExchange.getToCur(), currencyExchange.getRateCur(), quantity,
				quantity * currencyExchange.getRateCur()));
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(mono, CurrencyExchange.class)
				.switchIfEmpty(ServerResponse.notFound().build());
	}
}
Creating Router Functions

This is another important function of reactive stream as previously mentioned while writing Router Function.

To know more on Router Function you may read here and here.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.router;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.roytuts.spring.boot.reactive.currency.conversion.microservice.handler.CurrencyConversionHandler;
@Configuration
public class CurrencyConversionRouter {
	@Bean
	public RouterFunction<ServerResponse> route(CurrencyConversionHandler handler) {
		return RouterFunctions.route(RequestPredicates.GET("/currency-exchange/from/{from}/to/{to}/quantity/{quantity}")
				.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::getCurrencyExchange);
	}
}
Creating Main Class

Creating a main would be sufficient to deploy our application into the Tomcat server.

This is a great advantage that you just need to let Spring know that it is your Spring Boot Application using @SpringBootApplication and main class.

In the below class we have used @EnableFeignClients, because before we want to use feign client we must enable it using the @EnableFeignClients annotation on the appropriate package where the client proxy is defined.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients("com.roytuts.spring.boot.reactive.currency.conversion.microservice.proxy")
@SpringBootApplication(scanBasePackages = "com.roytuts.spring.boot.reactive.currency.conversion.microservice")
public class ReactiveCurrencyConversionMicroSvcApp {
	public static void main(String[] args) {
		SpringApplication.run(ReactiveCurrencyConversionMicroSvcApp.class, args);
	}
}
Creating Configuration File

We need to create src/main/resources/application.properties file to include some configurations.

We have put the application name, port and exclude null fields from JSON response.

We are running forex-microservice on port 8080, so we cannot run currency-conversion-microservice on the same port.

server.port=9090
spring.application.name=currency-conversion-microservice
spring.jackson.default-property-inclusion=NON_NULL
Running Currency Conversion Microservice

Run the main class to deploy our microservice into the server. Your server will start on port 8080.

Testing the Currency Conversion Microservice

Now call the REST service using any REST client or on browser:

USD to INR Total Amount

Request Method – GET

Request URL – http://localhost:9090/currency-exchange/from/EUR/to/INR/quantity/25

Response

{"id":"5d25cc66a17f66da6a7ab88e","fromCur":"EUR","toCur":"INR","rateCur":80.0,"quantity":25,"totalAmount":2000.0}

Distributing Clients Load

In the above examples we have created two microservices and built communication between them. However we have hardcoded the forex-microservice REST URL into currency-conversion-microservice and it means when new instance of forex-microservice is launched then we have no option to distribute the client side load.

Now we will see how to use Ribbon API to distribute the client side load.

Therefore first stop the currency-conversion-microservice if it is up and running already.

Add dependency for ribbon into build script of currency-conversion-microservice. The entire dependencies in build.gradle script looks as below:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux:${springBootVersion}")
    implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
    implementation("org.springframework.cloud:spring-cloud-starter-netflix-ribbon")
}

Enabling Ribbon

Build the currency-conversion-microservice and now enable RibbonClient in CurrencyConversionProxy interface as shown below.

As we want to have multiple instances of forex-microservice, so we have removed localhost:8080 from the @FeignClient.

package com.roytuts.spring.boot.reactive.currency.conversion.microservice.proxy;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.roytuts.spring.boot.reactive.currency.conversion.microservice.vo.CurrencyExchange;
@FeignClient(name = "forex-microservice")
@RibbonClient(name = "forex-microservice")
public interface CurrencyConversionProxy {
	@GetMapping("/forex/fromCur/{fromCur}/toCur/{toCur}")
	public CurrencyExchange getCurrency(@PathVariable(name = "fromCur") String fromCur,
			@PathVariable(name = "toCur") String toCur);
}

Leave a Reply

Your email address will not be published. Required fields are marked *