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);
}