Create Microservices using Spring Boot and build using Gradle

Introduction

Here we will see how to create Microservices using Spring Boot and Spring Cloud and build using Gradle tool.

What are Microservices?

Before I tell you about the microservices, I would like to tell you about monolithic architectural style, which is built as a single, autonomous unit. So let’s say an web application with client-server model, where the server-side application is a monolith application that handles request/response, executes business logic and work with the data in the underlying database.

Now why microservices came into picture? what if you come across some change or some sort of migration? Any modification made to a small part of a monolithic application requires deploying entirely the new version. That’s why, microservices came into picture.

Microservice, a.k.a, microservice architecture is an architectural style with an approach for developing a single application as a suite of small services, each running in its own process and communicating with a lightweight resource API that has the following characteristics:

  • Loosely coupled
  • Highly maintainable
  • Easily testable
  • Independently deployable
  • Built around business capabilities or single responsibilities
  • Fault isolation
  • Resilient
  • Scalable
  • Easy to integrate with third party APIs
  • Small and smart end-point

Popular Microservices frameworks:

  • Spring Boot (Best microservices framework in Java)
  • Flask (Popular microservices framework in Python)
  • Dropwizard (Java)
  • Spark framework (Java)
  • Swagger (Java)
  • Jersey (Java)
  • Seneca JS (Popular microservices framework in node.js)

Why Spring Boot is used in Microservices?

Microservices allow large systems to be built up from a number of collaborating components. It does at the process level what Spring has always done at the component level: loosely coupled processes instead of loosely coupled components and framework that has been evolved to be formidable for Java microservice development is Spring Boot that has the following advantages:

  • Spring Boot simplifies and bootstrapping application development.
  • Spring Boot makes it easy to develop production ready micro services.
  • Spring Boot has a component called Spring Boot Actuator, which makes it easy to monitor applications.

Challenges with the Microservices architecture:

  • Quick setup – you do not have a month to setup a microservice.
  • Automation – needs automation in every activity, such as, build, deployment, monitoring etc. due to number of smaller components.
  • Visibility – you need a great visibility around every component for monitoring and identifying problems automatically.
  • Boundary – understanding of the domain evolves over a period of time. You need to ensure that the microservice boundaries evolve with time.
  • Configuration – better to have configuration management when you need to maintain configurations for hundreds of components across environments.
  • Debugging – centralized logging is essential as you may need to investigate the problem into multiple services across different components.
  • Scalability – advantage of microservices can be realized if the application can scale up and down easily in the cloud.

Enough talks! Now we will move on to create microservices using Spring Boot 2.1 and Spring Cloud and build using Gradle.

Prerequisites

Spring Boot 2.2.6, Java at least 1.8, Gradle 6.1.1, Eclipse

Example with Source Code

We assume that you already have at least Java 1.8 and Gradle 6.1.1 installed and configured appropriately.

In this tutorial we will create two microservices – Forex Service and Currency Conversion Service.

Forex Service – let’s say it provides currency exchange values for various currencies.

An example could be shown as below:

GET http://localhost:9000/forex-exchange/from/USD/to/INR

{
  "id": 1,
  "from": "USD",
  "to": "INR",
  "rate": 71.0
}

Currency Conversion Service – let’s say it provides total calculated amount from one currency to another currency using Forex Service. So Currency Conversion Service is the consumer.

As example could be written as below:

GET http://localhost:9100/currency-converter/from/USD/to/INR/quantity/100

{
  "id": 1,
  "from": "USD",
  "to": "INR",
  "rate": 71.0,
  "quantity": 100,
  "totalAmount": 7100.0
}

Microservice – Forex Service

Project Name

The project’s name is forex-service that has been created in Eclipse IDE.

Build script

Create a Gradle project in Eclipse IDE and modify the build.gradle script with the following content:

buildscript {
	ext {
		springBootVersion = '2.2.6.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id "io.spring.dependency-management" version "1.0.9.RELEASE"
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
    
sourceCompatibility = 12
targetCompatibility = 12

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	implementation("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
	runtime("com.h2database:h2:1.4.200")
}

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR3'
    }
}

In the above build script we see that we have added Spring Boot.

We have added web starter, data jpa starter and H2 database dependencies.

We have included spring cloud version Hoxton.SR3.

You can also read Integrate H2 in-memory database with Spring Boot 2.1 and build using Gradle

Creating Entity class

package com.roytuts.forex.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class ForexValue {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	@Column(name = "from_curr")
	private String from;
	@Column(name = "to_curr")
	private String to;
	private Double rate;
	public ForexValue() {
	}
	public ForexValue(String from, String to, Double rate) {
		this.from = from;
		this.to = to;
		this.rate = rate;
	}
        //getters and setters
}

In the above class @Entity specifies that the class is entity class. @Id specifies the primary key of the entity class.

We want the auto-increment values for the corresponding primary key into table column, so we are using @GeneratedValue(strategy = GenerationType.IDENTITY).

Further we have not annotated all class attributes with @Column but only those columns which are different from class attribute name.

Creating Spring Data JPA Repository

package com.roytuts.forex.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.roytuts.forex.entity.ForexValue;
public interface ForexRepository extends JpaRepository<ForexValue, Integer> {
	ForexValue findByFromAndTo(String from, String to);
}

We extend the JpaRepository interface to get advantage of the Spring’s built-in API.

We pass ForexValue as an entity and Integer to denote primary key data type.

We find the conversion value from one currency to another currency using the method findByFromAndTo().

Creating the Spring REST Controller

Create below Spring REST controller to define a resource URI.

package com.roytuts.forex.rest.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.roytuts.forex.entity.ForexValue;
import com.roytuts.forex.repository.ForexRepository;
@RestController
public class ForexRestController {
	@Autowired
	private ForexRepository forexRepository;
	@GetMapping("/forex-exchange/from/{from}/to/{to}")
	public ResponseEntity<ForexValue> getCurrencyExchange(@PathVariable String from, @PathVariable String to) {
		ForexValue forexValue = forexRepository.findByFromAndTo(from, to);
		return new ResponseEntity<ForexValue>(forexValue, HttpStatus.OK);
	}
}

We create the end-point as /forex-exchange/from/{from}/to/{to} for converting one currency to another currency value.

We query the database and find the appropriate conversion rate from the table.

Creating main class

Create Spring Boot main class to deploy and start the application.

package com.roytuts.forex.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EntityScan("com.roytuts.forex.entity")
@EnableJpaRepositories("com.roytuts.forex.repository")
@SpringBootApplication(scanBasePackages = "com.roytuts.forex")
public class ForexApplication {
	public static void main(String[] args) {
		SpringApplication.run(ForexApplication.class, args);
	}
}

We let the container know where our Repository interface and Entity class.

Creating application.yml

Create application.yml file under src/main/resources to put some configurations:

spring:
   application:
      name: forex-service
   jpa:
      show-sql: true
   h2:
      console:
         enabled: true
server:
   port: 9000

We enabled the H2 console and we also want to see the database query in the console.

We want embedded Tomcat server at port 9000.

Insert test data into database

We want to test our application, so we want to insert some test data into the database table.

Create sql script called data.sql under src/main/resources with the following sql statements:

insert into forex_value(from_curr,to_curr,rate)
values('USD','INR',71);
insert into forex_value(from_curr,to_curr,rate)
values('EUR','INR',85);
insert into forex_value(from_curr,to_curr,rate)
values('AUD','INR',39);

Testing the Forex Microservice

Run the main class. When your main class successfully runs, you should find message in the Eclipse console that your table has been created:

Hibernate: drop table forex_value if exists
Hibernate: create table forex_value (id integer generated by default as identity, from_curr varchar(255), rate double, to_curr varchar(255), primary key (id))

Your data.sql script gets executed also during application deployment.

You will find your embedded Tomcat started on port 9000:

Tomcat started on port(s): 9000 (http) with context path ''

Now if you call GET request on http://localhost:9000/forex-exchange/from/USD/to/INR, then you will see below output

{"id":1,"from":"USD","to":"INR","rate":71.0}

You will find the database query got executed by looking at the log message into the Eclipse console:

Hibernate: select forexvalue0_.id as id1_0_, forexvalue0_.from_curr as from_cur2_0_, forexvalue0_.rate as rate3_0_, forexvalue0_.to_curr as to_curr4_0_ from forex_value forexvalue0_ where forexvalue0_.from_curr=? and forexvalue0_.to_curr=?

Microservice – Currency Conversion Service

Project Name

The project’s name is currency-conversion-service that has been created in Eclipse IDE.

Build script

Create a Gradle based project in Eclipse and modify the build.gradle script to match the below content:

buildscript {
	ext {
		springBootVersion = '2.2.6.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id "io.spring.dependency-management" version "1.0.9.RELEASE"
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
    
sourceCompatibility = 12
targetCompatibility = 12

repositories {
	mavenLocal()
    mavenCentral()
}

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

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR3'
    }
}

Creating a response bean

Create below bean for response that has to be sent to the clients or end users.

package com.roytuts.cc.model;
public class CurrencyConversion {
	private Integer id;
	private String from;
	private String to;
	private Double rate;
	private Integer quantity;
	private Double totalAmount;
	public CurrencyConversion() {
	}
	public CurrencyConversion(Integer id, String from, String to, Double rate, Integer quantity,
			Double totalAmount) {
		this.id = id;
		this.from = from;
		this.to = to;
		this.rate = rate;
		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.

package com.roytuts.cc.proxy;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.roytuts.cc.model.CurrencyConversion;
@FeignClient(name = "forex-service", url = "localhost:9000")
public interface CurrencyConversionProxy {
	@GetMapping("forex-exchange/from/{from}/to/{to}")
	public CurrencyConversion retrieveExchangeValue(@PathVariable("from") String from, @PathVariable("to") String to);
}

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

@GetMapping("forex-exchange/from/{from}/to/{to}") indicates the URI of the service we want to consume.

Creating Spring REST controller class

Create below Spring REST Controller class to consume the forex-service.

package com.roytuts.cc.rest.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.roytuts.cc.model.CurrencyConversion;
import com.roytuts.cc.proxy.CurrencyConversionProxy;
@RestController
public class CurrencyConversionRestController {
	@Autowired
	private CurrencyConversionProxy currencyConversionProxy;
	@GetMapping("currency-converter/from/{from}/to/{to}/quantity/{quantity}")
	public ResponseEntity<CurrencyConversion> getCurrencyExchange(@PathVariable String from, @PathVariable String to,
			@PathVariable Integer quantity) {
		CurrencyConversion response = currencyConversionProxy.retrieveExchangeValue(from, to);
		return new ResponseEntity<CurrencyConversion>(new CurrencyConversion(response.getId(), from, to,
				response.getRate(), quantity, quantity * response.getRate()), HttpStatus.OK);
	}
}

Making the call using the proxy is very simple. All we need to do was to autowire the proxy and use to call the method.

Creating application.yml

Create below application.yml file under src/main/resources to define some configurations, such as, application name, server port etc.

spring:
   application:
      name: currency-conversion-service
server:
   port: 9100

Creating main class

Create below main class to deploy the application into embedded Tomcat server and start the application.

package com.roytuts.cc.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient
@EnableFeignClients("com.roytuts.cc.proxy")
@SpringBootApplication(scanBasePackages = "com.roytuts.cc")
public class CurrencyConversionApplication {
	public static void main(String[] args) {
		SpringApplication.run(CurrencyConversionApplication.class, args);
	}
}

In the above 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.

Testing the application

Run the main class. You will see the following message in the console when server gets started:

Tomcat started on port(s): 9100 (http) with context path ''

Once the application gets deployed into embedded Tomcat server then call the below URL in REST client or any other REST utility tool to test the application.

GET http://localhost:9100/currency-converter/from/USD/to/INR/quantity/100

{"id":1,"from":"USD","to":"INR","rate":71.0,"quantity":100,"totalAmount":7100.0}

You will see following message in the console of forex-service application.

Hibernate: select forexvalue0_.id as id1_0_, forexvalue0_.from_curr as from_cur2_0_, forexvalue0_.rate as rate3_0_, forexvalue0_.to_curr as to_curr4_0_ from forex_value forexvalue0_ where forexvalue0_.from_curr=? and forexvalue0_.to_curr=?

In the above examples we have created two microservices and built communication between them. However we have hardcoded the forex-service REST URL into currency-conversion-service and it means when new instance of forex-service 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-service if it is up already.

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

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

Build the currency-conversion-service and now enable RibbonClient in CurrencyConversionProxy interface as shown below:

package com.roytuts.cc.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.cc.model.CurrencyConversion;
@FeignClient(name = "forex-service")
@RibbonClient(name = "forex-service")
public interface CurrencyConversionProxy {
	@GetMapping("forex-exchange/from/{from}/to/{to}")
	public CurrencyConversion retrieveExchangeValue(@PathVariable("from") String from, @PathVariable("to") String to);
}

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

Now let’s say we want to run forex-service on two instances, so configure the instances in application.yml file under src/main/resources of currency-conversion-service. The entire application.yml file looks as below:

spring:
   application:
      name: currency-conversion-service
server:
   port: 9100
forex-service:
   ribbon:
      listOfServers: localhost:9000,localhost:9001

It’s obvious from the above file that our forex-service will run on localhost:9000 and localhost:9001. So we have to configure forex-service to run on 9001 port because the forex-service already running on port 9000.

First stop the forex-service if already running. Then configure forex-service to run on port 9001 port. To configure another launch program fro forex-service do the following:

Do right click on the forex-service and select Run As -> Run Configurations…

When a popup window opens, under the Java Application, search ForexApplication and do right click on this and click on Duplicate.

Change the Name and put server port in VM arguments section under Arguments tab as shown in below image:

microservices using spring boot and spring cloud and build using gradle

Now run two instances of forex-service and one instance of currency-conversion-service by executing main class.

So forex-service instances are up on 9000 and 9001 ports, whereas currency-conversion-service instance is up on port 9100.

Now make below requests:

GET http://localhost:9100/currency-converter/from/USD/to/INR/quantity/100

{"id":1,"from":"USD","to":"INR","rate":71.0,"quantity":100,"totalAmount":7100.0}

The above response come from the instance of forex-service running on port 9000. You will see the message on the Eclipse console:

Hibernate: select forexvalue0_.id as id1_0_, forexvalue0_.from_curr as from_cur2_0_, forexvalue0_.rate as rate3_0_, forexvalue0_.to_curr as to_curr4_0_ from forex_value forexvalue0_ where forexvalue0_.from_curr=? and forexvalue0_.to_curr=?

GET http://localhost:9100/currency-converter/from/USD/to/INR/quantity/1000

{"id":1,"from":"USD","to":"INR","rate":71.0,"quantity":1000,"totalAmount":71000.0}

The above response comes from the instance of forex-service running on port 9001. You will see the message on the Eclipse console:

Hibernate: select forexvalue0_.id as id1_0_, forexvalue0_.from_curr as from_cur2_0_, forexvalue0_.rate as rate3_0_, forexvalue0_.to_curr as to_curr4_0_ from forex_value forexvalue0_ where forexvalue0_.from_curr=? and forexvalue0_.to_curr=?

We are using Ribbon to distribute the load between the two instances of forex-service.

However, we are hard-coding the URLs of both instances of forex-service in currency-conversion-service. Therefore every time if there is a new instance of forex-service, we need to change the configuration of currency-conversion-service and it’s not what we want.

So we will use Eureka Naming Server to fix this problem.

Spring Boot project – eureka-server-config

Now we will create another Spring Boot project in Eclipse. So create Gradle based project in Eclipse. The project’s name is eureka-server-config.

Build script

Modify the build.gradle script of eureka-server-config as show below:

buildscript {
	ext {
		springBootVersion = '2.2.6.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id "io.spring.dependency-management" version "1.0.9.RELEASE"
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
    
sourceCompatibility = 12
targetCompatibility = 12

repositories {
	mavenLocal()
    mavenCentral()
}

dependencies {
	implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-server")
}

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR3'
    }
}

In the above build script we have added eureka server.

Creating application.yml

Now add below content to src/main/resources/application.yml of eureka-server-config project:

spring:
   application:
      name: eureka-server-config
server:
   port: 8761
eureka:
   client:
      register-with-eureka: false
      fetch-registry: false

Notice we have added two properties with false value. If we do not make them false (as by default they are true), you may face following exception while running Eureka server:

com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

eureka.client.register-with-eureka=false: if we make this property true then while the server starts the inbuilt client will try to register itself with the Eureka server.

eureka.client.fetch-registry=false: if we make this property true then the inbuilt client will try to fetch the Eureka registry, which is not yet available. As a result, we would get TransportException.

Creating main class

Create below main class in order to deploy application into embedded eureka server on port 8761.

package com.roytuts.esc.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerConfigApplication {
	public static void main(String[] args) {
		SpringApplication.run(EurekaServerConfigApplication.class, args);
	}
}

Now run the main class to deploy eureka-server-config application. Your eureka server starts on port 8761.

If you hit the URL http://localhost:8761 in the browser, you will get below page.

microservices using spring boot and spring cloud and build using gradle

Currently there is no instance running on eureka server.

Now we will connect forex-service and currency-conversion-service microservices using eureka server.

Now add below dependency to both of the build scripts – forex-service and currency-conversion-service.

compile("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client")

Make sure you stop the microservices before you add to build script and later build the microservices.

Next we need to configure eureka URL into application.yml file of both microservices – forex-service and currency-conversion-service:

eureka:
   client:
      service-url:
         default-zone: http://localhost:8761/eureka

Now remove the following configuration from src/main/resources/application.yml file of currency-conversion-service:

forex-service:
   ribbon:
      listOfServers: localhost:9000,localhost:9001

Now change the ForexApplication main class to add @EnableDiscoveryClient annotation. We need this annotation to register with eureka server. The @EnableDiscoveryClient activates the Netflix Eureka DiscoveryClient implementation.

@EnableDiscoveryClient
@EntityScan("com.roytuts.forex.entity")
@EnableJpaRepositories("com.roytuts.forex.repository")
@SpringBootApplication(scanBasePackages = "com.roytuts.forex")
public class ForexApplication {

Now run two instances of forex-service and one instance of currency-conversion-service.

Now refresh the eureka server page URL – http://localhost:8761, you will see one instance of currency-conversion-service and two instances of forex-service are up as shown below in the image:

microervices using spring boot and spring cloud and build using gradle

We have now created two microservices – forex-service & currency-conversion-service and established communication between them.

We are using Ribbon to distribute load between the two instances of forex-service and Eureka as the naming server.

When we launch new instances of forex-service, you would see that load is automatically distribute to them.

Now you can continue the same tests you performed before we created eureka-server-config application.

That’s all on how to create microservices using Spring Boot and Spring Cloud and build using Gradle tool.

Source Code

download source code

Thanks for reading.

1 thought on “Create Microservices using Spring Boot and build using Gradle

Leave a Reply

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