Skip to content

  • Home
  • Core Java
    • Write Your Own Immutable Class In Java
    • Write Your Own Singleton Class In java
    • Java Concurrent Package
    • Java Stream Revisited
    • Print odd and even numbers using thread
    • SOLID principles
    • Comparable Vs Comparator
    • Sort HashMap/TreeMap based on value
    • Deep and Shallow Copy in Java with examples
    • Find the frequency of each character in a String
    • How to avoid duplicate Employee objects in HashSet ?
  • Spring
    • Loose Coupling & Dependency Injection
    • Bean Scope
    • Spring Bean Lifecycle
    • IoC Container Vs Application Context Vs Bean Factory
    • @Component Vs @Service @Repository Vs @Controller
    • How to read properties file in Spring
    • Spring AOP (Aspect Oriented Programming)
    • @Component Vs @Bean in Spring
    • Exception Handling in SpringBoot
    • XML configuration Vs Annotations configuration in Spring
    • Spring Data JPA
    • Spring Data REST
  • Spring Security
    • Spring Security with Form, Basic and JWT
    • Security Configuration in Spring Boot Apps
    • Security Protocols & Frameworks
    • Okta OAuth 2.0 SSO with Springboot
    • Spring Boot 2.x Security using Username Password
    • Spring Boot 2.x Security using JWT
    • Spring Boot 3.x Security using Username Password
    • Spring Boot 3.x Security using JWT
  • Microservices
    • Spring Cloud Config (Server & Client)
    • Spring Boot Microservices Tutorial (Part 1 of 2)
    • Spring Boot Microservices Tutorial (Part 2 of 2)
    • Circuit Breaker – Resilience4j
    • The Twelve-Factor App Principles
  • Event Driven Microservices
    • What is Event Driven Microservice ?
    • What is Saga Design Pattern ?
    • What is CQRS Design Pattern ?
  • Spring AI
    • ChatGPT API + SpringBoot Integration
  • Hibernate & JPA
    • JPA vs JDBC
    • CRUD Example Using Spring Boot JPA + H2 + Gradle
    • MongoDB Atlas With Spring Boot Example
    • Transaction Management
    • Relationships in JPA & Hibernate
    • Hibernate First & Second Level Cache
    • Spring Boot Flyway Postgres
  • DevOps
    • What is Devops ?
    • Docker
    • Kubernetes (K8S)
    • Jenkins
    • Infrastructure As Code
  • Functional Programming
    • Functional Programming Vs Structured Programming
    • Java 8 Programs For Interview
    • Predicate, Function, Consumer and Supplier in Java 8
    • Sort a List having Employee objects using Java 8
    • Find Employee from List using Java 8
  • AWS
    • AWS S3
    • AWS EC2
    • EC2 Solutions Architecting
    • How to create an EC2 instance ?
    • How to connect to AWS EC2 instance ?
    • Deploy application to AWS
    • AWS Lambda Java Tutorial
    • Spring Cloud Functions
    • How to Start/Stop AWS ECS automatically using Java Spring?
    • Container Solution in AWS
    • AWS SQS, SNS, MQ and Kinesis
    • Databases in AWS
    • AWS VPC: Peering, Endpoint, VPN, Gateways- Internet, NAT, DX
    • Machine Learning in AWS
    • Storage Solutions in AWS
    • AWS ASG (Auto Scaling Group)
  • AWS Certifications
    • SAA-C03
      • Design Cost-Optimized Architectures
    • AWS Certified Solution Architect-Associate
      • Question 1
  • Kafka
    • Apache Kafka
    • Kafka Producer & Consumer Example Using Spring boot & Conduktor
  • Angular
    • Angular Tutorial – Part 1
    • Angular Tutorial – Part 2
  • Miscellaneous
    • How to add a project to Git/GitHub/GitLab
    • How to Clone a project from Git/GitLab using Git Bash
    • How to query Oracle tables based on some date ?
    • How to highlight text in WordPress ?
    • How to add Page Jumps in WordPress page ?
  • Interview Preparation
    • Core java interview questions
    • Java Threads Interview Questions
  • Contact Me
  • Toggle search form

Spring Boot Microservices Tutorial (Part 2 of 2)

This tutorial is the continuation of the previous tutorial of Spring Boot Microservices Tutorial (Part 1)

Custom Routes in API Gateway :

One of the option to create custom routes is by creating a configuration file. Lets create a new class: ApiGatewayConfiguration

@Configuration
public class ApiGatewayConfiguration {
	@Bean
	public RouteLocator gatewayRouter(RouteLocatorBuilder builder) {
		
        Function<PredicateSpec, Buildable<Route>> routeFunction = p -> p.path("/get").uri("http://httpbin.org/"); //any working URL.

		return builder.routes()
				 .route(routeFunction)
				 .build();
	}
}

Now we will customize our earlier URLs.

Function<PredicateSpec, Buildable<Route>> routeFunction2 = p -> p.path("/get").uri("http://heapsteep.com"); //any working URL.
Function<PredicateSpec, Buildable<Route>> routeFunction3 = p -> p.path("/microservice-2/**").uri("lb://microservice-2");
Function<PredicateSpec, Buildable<Route>> routeFunction4 = p -> p.path("/microservice-2-feign/**").uri("lb://microservice-2");

Not only I am redirecting to a particular naming server, we are also load balancing it.

I will comment out the below 2 lines in application.properties :

#spring.cloud.gateway.discovery.locator.enabled=true
#spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true

So, for the below URL:
http://localhost:8765/microservice-2/microservice-2/from/USD/to/INR/quantity/10
the new URL would be:
http://localhost:8765/microservice-2/from/USD/to/INR/quantity/10

And for below URL:
http://localhost:8765/microservice-2/microservice-2-feign/from/USD/to/INR/quantity/10
the new URL would be:
http://localhost:8765/microservice-2-feign/from/USD/to/INR/quantity/10

Spring Cloud Sleuth :

It helps in assigning a TraceId and SpanId to each request.

Lets do a hands-on. Create a project having below dependency:
* Web
* Spring Cloud Sleuth


Below lines will be automatically added for Spring Cloud Sleuth in the pom.xml:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

Lets create a REST method:

@RestController
public class MyController {
	
	private static final Logger logger=LoggerFactory.getLogger(TestController.class);
	
	@GetMapping("/service1")
	public String service1() {
		logger.info("service1 called ....");
		return "service1...";
	}
}

And create a configuration class:

@Configuration
public class CloudConfig {
	@Bean
	public Sampler defaultSampler() {
		return Sampler.ALWAYS_SAMPLE;
	}
}

That’s all…

Just call any url from a browser:   http://localhost:8080/service1
In the log one can see below text:

2020-07-23 19:36:18.414  INFO [, 4bfc21f0583bc3e9 , 4bfc21f0583bc3e9 ,true]

In the above text one can see 4 parameters- the 1st one is blank, the 2nd and 3rd are some alpha numeric value and 4th one is a boolean value. so those values are basically :
[application-name, traceId, spanId, export flag]
Since I had not mentioned the application name in application.properties file, its coming as blank. If you had mentioned the same then you may see like below:
2020-07-23 19:36:18.414  INFO [MyApplication_1, 4bfc21f0583bc3e9 , 4bfc21f0583bc3e9,true]

Now lets do a change….

Lets call this service from another service. For simplicity, here we will not call from another microservice application, rather we will give a call from another API endpoint.
So, add another method – service2() as shown below in the code:

@RestController
public class MyController {
	
	private static final Logger logger=LoggerFactory.getLogger(TestController.class);

	@Autowired
	private RestTemplate restTemplate;
		
	@GetMapping("/service1")
	public String service1() {
		logger.info("service1 called ....");
		return "service1...";
	}
	
	@GetMapping("/service2")
	public String service2() {
		logger.info("service2 called ....");
		return restTemplate.getForObject("http://localhost:8080/service1", String.class);
	}

}

Some more changes:

@Configuration
public class CloudConfig {

	@Bean
	public RestTemplate template() {
		return new RestTemplate();
	}

	@Bean
	public Sampler defaultSampler() {
		return Sampler.ALWAYS_SAMPLE;
	}
}

Once you run the application and call the service-2, you will see below things in the console log:

INFO [, 2cc8cdabafe90344 , 2cc8cdabafe90344 , true] 17444 service1 called ….
INFO [, 2cc8cdabafe90344 , 2723470714fe6043 , true] 17444 service2 called ….

If you see the SpanId of the first call becomes the TraceId of the 2nd call.

The above logs are now visible in the logs of local machine. Lets go a step further to post these Ids to a server called – Zipkin.

The above source can be found at : https://github.com/heapsteep/spring-cloud-sleuth.git

Zipkin :

It is the server for distributed tracing system.

You need to download the zipkin jar from the official site.
Then just run it wherever you want (even in download folder as well):  

jar -jar zipkin.jar

To verify whether Zipkin is running call the below URL:
http://localhost:9411

One can see the home page of the Zipkin server.

To add the call traces of your microservices to the Zipkin server, you need to add Zipkin Client dependency to the same microservice.
So, add below dependency from Spring Initializr:

After adding the above dependency one can see below item in pom.xml:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

Add below item in application.properties:

spring.zipkin.base-url=http://localhost:9411

call the service from browser: http://localhost:8080/service2

You can see the traces in Zipkin dashboard.

That’s all !…

Feign :

Feign is a REST Service Client. Generally when we have to call a microservice from another microservice we take help of RestTemplate. But using a RestTemplate make it kinda hardcoding and we have to write a lot of stuff. We can avoid that by using Feign.

Lets see an example.

Suppose there is a microservice called – book-service. And it has below APIs:
http://localhost:8000/api/getAllBooks

http://localhost:8000/api/getBookDetail/1

You can check out the source code of book-service application here: https://github.com/heapsteep/book-service.git
You may find some code commented in the repository, just un-comment it to test it.

Now if we want to call the above book-service from customer-service, we will first use RestTemplate as show below:

The controller:

@RestController
public class CustomerController {
	@GetMapping("/getBookDetail")
	public BookInfo getBookDetail() {
		ResponseEntity responseEntity= new RestTemplate().getForEntity("http://localhost:8000/api/getBookDetail/1", BookInfo.class);
		BookInfo bookInfo=(BookInfo)responseEntity.getBody();
		return bookInfo;
	}
@Data
@Setter @Getter
public class BookInfo {
	private String bookTitle;
    private int port;
}

This customer-service runs on port: 8100.
Call this URL to see the result: http://localhost:8100/getBookDetailFeign/1

In the above example we can replace it with Feign.

Lets follow the steps to enable Feign in customer-service. You don’t have to do anything in the book-service, unless and until you have to call another service from book-service.

  1. Add below Feign dependency. Feign dependencies are not available in Spring Initializer. You need to manually add it.
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>

2. In main class add this: @EnableFeignClients(“com.heapsteep”)

3. Create a proxy interface. The name is the application you are supposed to call- this name you will find from application.properties. Give the signature and GetMapping parameters of the method same as of the calling method.

@FeignClient(name="book-service", url="localhost:8000")
public interface BookServiceProxy{
			@GetMapping("/api/getBookDetail/{id}")
	public BookInfo getBookDetail(@PathVariable Long id);
}

4. In the controller add below method:

@GetMapping("/getBookDetailFeign/{id}")
public BookInfo getBookDetailFeign(@PathVariable("id") Long id) {		
		return proxy.getBookDetail(id);		
}

Thats it….

customer-service source code can be found here: https://github.com/heapsteep/customer-service.git

Netflix Ribbon :

Ribbon is used for client side Load Balancing.

In Springboot 2.4 version and beyond, Ribbon is not required. Only using Feign will do load balancing. Just register the microservices in Naming Server, rest feign will take care. Basically the spring-cloud-starter-loadbalancer is present in the Eureka Client.

Ribbon has to be used with Feign, why because if you are using RestTemplate you are hard coding the port in the call, so you can’t see the effect of Ribbon. So, before proceeding further you should have idea about Feign.

Below are the steps to add Ribbon to your calling application(the client):

  1. Add Ribbon dependency from spring Initializer.
  2. In application.propperties add the 2 instances of the server application: (make sure 2 server application is running)

Thats it…

As usual, call this URL: http://localhost:8100/getBookDetailFeign/1
We can see that the Ribbon is distributing load between 8000 and 8001 port.

Customer-service source code:

The controller:

@RestController
public class CustomerController {	
	@Autowired
	private BookServiceProxy proxy;
		
	@GetMapping("/getBookDetailFeign/{id}")
	public BookInfo getBookDetailFeign(@PathVariable("id") Long id) {		
		return proxy.getBookDetail(id);		
	}
}

The model: BookInfo.java

@Data
@Setter @Getter
public class BookInfo {
	private String bookTitle;
    private int port;
}

The proxy:

@FeignClient(name="book-service")
public interface BookServiceProxy {
	
	@GetMapping("/api/getBookDetail/{id}")
	public BookInfo getBookDetail(@PathVariable Long id);

}

application.properties:

service-name.ribbon.listOfServers=http://localhost:8000,http://localhost:8001

Netflix Eureka Naming Server :

Eureka Naming Server is used to register different services.

Lets do a demo of it.

Create a new spring boot project. Add “Eureka Server” from Spring Initializer.

Add @EnableEurekaServer to the main class.

In application.properties:

spring.application.name=eureka-naming-server
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Below changes to be done in other microservices:

Add this dependency in pom.xml: Eureka Discovery Client
P.S. : Whenever you are adding any “Cloud” specific dependency, don’t forget to add below items as well:

<spring-cloud.version> Hoxton.SR7</spring-cloud.version>

<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
</dependencyManagement>


Add in main class: @EnableDiscoveryClient

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

Disable this:

#springboot-jpa-mysql.ribbon.listOfServers=http://localhost:8000,http://localhost:8001

Here is the source code of the eureka-naming-server application in GitHub:
https://github.com/heapsteep/eureka-naming-server.git

Copyright © 2025 .

Powered by PressBook Blog WordPress theme