Skip to main content

Back-End Development

Tutorial 04 – Microservices – Discovery Client, LoadBalancer Client and Feign Client

Transportation And Technology Concept. Its (intelligent Transport Systems). Mobility As A Service.telecommunication. Iot (internet Of Things). Ict (information Communication Technology).

Before you dive into this tutorial, make sure to check out the other blogs in this series:

In the Eureka Server environment, 3 “Client Component” types are possible:

  1. Discovery Client Component (Legacy, No Support for Load Balancing).
  2. Load Balancer Client Component (Good, Perform Load Balancing).
  3. Feign Client Component (Best, Support All Approached, and Load Balancing).

Discovery Client

Limitations of Discovery Client type client component:

  • We receive a list of target microservice instances, from which we must manually select one. However, we desire one instance of the target microservice (producer microservice) that has a lower load factor. [Load balancing is not feasible].
  • This basic client component, often known as the client type component, is a legacy. i.e., it does not meet industry standards.
  • Collecting one instance from the list of instances is the pure responsibility of the consumer application, which is a manual process. So, other instances of the target may sit idle.

To solve these problems, use the “LoadBalancerClient” type client component.

Producer Microservice Controller:

package com.boot.versioning;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(name = "/billing/api")
public class BillingInfoController {
    
    public ResponseEntity<String> fetchBillingDetails(){
        
        // Returning dummy data.
        return new ResponseEntity<String>("Final billAmount = billAmount - discount (RS. 10000)",HttpStatus.OK);
    }

}

Consumer microservice client:

@Component
public class BillingServiceConsumerClient {
    
    private DiscoveryClient client;
    
    public String getBillingInfo() {
        
        //Get Billing-Service instance from Eureka Server
        List<ServiceInstance> listInstances = client.getInstances("Billing-Service");
        // Get single instance from list of instance (no load balancing)
        ServiceInstance instance = listInstance.get(0);
        // get details from Service Instance
        URI uri = instance.getUri();
        //prepare provider MS related url to consumer method
        String url = uri.toString()+"/billing/api/info";
        
        //create RestTemplate class obj to consume the provider service
        RestTemplate template = new RestTemplate();
        //consume the provider service
        ResponseEntity<String> response = template.getForEntity(url, String.class);
        //get responseContent from ResponseEntity object
        String responseContent = response.getBody();
        
        return responseContent;
    }

}

LoadBalancerClient

  • This is another client-type component that selects the least load factor instance even though there are many instances for targeted microservices, i.e., we never receive a list of instances even if there are multiple instances for producer microservices. We always receive one instance of the producer microservice, which has a low load factor.
  • Spring Cloud Netflix provides an implementation of the “LoadBalancerClient” interface through the “RibbonLoadBalancerClient” class, which can be injected into the Consume application using AutoConfiguration.
  • The method chooses (-) which instance ID called on the “LoadBalancerClient” object will bring the less load factor instance of the target microservice.

To generate numerous instances of any microservice, execute the microservice application many times with various port numbers because two servers or microservices on the same computer cannot utilize the same port number, but they may be shared across other machines.

An Example Application Using LoadBalancerClient Type Client Component (Microservice Intra Communication Using LoadBalancerClient):

1. Develop the Eureka Server application.

2. Develop producer microservices.

  • Because we want to execute the producer program numerous times, it is best to assign a unique name to each instance, often “serviceID:<random.value>”.
    • Create a project using the Spring Web and EurekaDiscoveryClient dependencies. (Do not include dev tool dependencies.)
    • Add @EnableEurekaClient at the top of the main class.
    • Add the following entries to application.properties:
#Port number
server.port = 9900
#Service ID
spring.application.name = Billing-Service
#Eureka Server publishing info
eureka.client.server-url.default-zone = http://localhost:8761/eureka
#Provide application name + random value as Instance ID
eureka.instance.instance-id = ${spring.application.name}:${random.value}

3. Create a Rest Controller that acts as a Producer microservice.

//BillingInfoController.java (Client Component)
package com.perficient.rest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/billing/api")
public class BillingInfoController {
    @Value("${server.port}")
     private  int port;
    @Value("${eureka.instance.instance-id}")
      private  String instanceid;
    
    @GetMapping("/info")
    public  ResponseEntity<String>  fetchBillingDetails(){
       return new ResponseEntity<String>(" Final BillAmt= BillAmt- discount (Rs.5000) :: using instance::-->"+instanceid+" @port::"+port,HttpStatus.OK);   
    }

}

4. Create the consumer application with the help of LoadBalancerClient.

  • Create a project with Spring Web and EurekaDiscoveryClient dependencies; Ribbon will appear directly (Do not include development tool requirements).
  • Add @EnableEurekaClient at the top of the main class.

5. Create a helper with LoadBalancerClient component injection.

//BillingServiceConsumerClient .java (Client App)
package com.perficient.client;

import java.net.URI;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class BillingServiceConsumerClient {
    @Autowired
    private  LoadBalancerClient client;
    
      public   String   getBillingInfo() {
          // Get Billing-Service  Instance from eureka server  based LoadFactor
          ServiceInstance instance=client.choose("Billing-Service");
           // get details from Serivce Instance
           URI uri=instance.getUri();
            //prepare  provider MS related url to cosume method 
          String url=uri.toString()+"/billing/api/info";
          
         //create RestTemplate class obj to cosume the provider  service
            RestTemplate template=new RestTemplate();
         //  consume the provider service
            ResponseEntity<String> response=template.getForEntity(url,String.class);
           // get response content from ResponseEntity object
            String responseContent=response.getBody();
          
            return responseContent;
          
      }

}

6. Create the Rest Controller

//ShoppingSerivceOperationsController.java (RestController)
package com.perficient.rest;

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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.nt.client.BillingServiceConsumerClient;

@RestController
@RequestMapping("/shopping/api")
public class ShoppingSerivceOperationsController {
     @Autowired
    private BillingServiceConsumerClient client;

     @GetMapping("/cart")
      public   ResponseEntity<String>  doShopping(){
          //use  Client Comp
          String  resultMsg=client.getBillingInfo();
          try {
              Thread.sleep(20000);
          }
          catch(Exception e) {
              e.printStackTrace();
          }
          return  new ResponseEntity<String>("Shopping the items(shirt,trouser) ::::"+resultMsg,HttpStatus.OK);
          
        
              
          
          
      }
      
}

7. Run the application in the following sequence:

  1. Run the Eureka Server application.
  2. Run the producer application multiple times but change the port number. [server.port value]
  3. Run the consumer application.

Loadbalancerclient

 

Limitations with Discovery Client and Load Balancer Client

  • Eureka Server allows them to locate and get producer microservice instances as well as other information. However, they cannot make http calls to communicate with the desired microservices; so, we must utilize RestTemplate separately.
  • We use hardcoding to locate and get the target microservice instance.

Feign Client

  • It is termed an abstract client because we just supply an interface with method definition and annotation. However, the full logic will be built using the InMemory dynamic proxy class [Proxy design Pattern].
  • It is a combination client, which means that it obtains the target microservice instance from Eureka Server while simultaneously communicating with the target microservice via http calls generated without the need for coding. (The full code will be created by the InMemory proxy class.)
  • It uses LoadBalancerClient internally, which means it selects the target microservice instance from a list of service instances with a lower load factor.
  • We do not create a helper RestController for the consumer RestController, but rather an Interface with @FiegnClient(“ServiceId”), which generates an InMemory dynamic proxy class object that will be injected into the consumer RestController.
  • While dealing with FeignClient, we need to include two specific annotations along with ordinary annotations in the consumer application.
    • @EnableFeignClient at the start of the main class, beside @EnableEurekaClient.
    • @FeignClient at the beginning of the interface indicates that a dynamic in-memory proxy class will be produced inside.

Feignclient Rules

The Spring Cloud setup produces the Dynamic InMemory Proxy class for this interface, which implements the getBillingInfo() function and includes logic to use LoadBalancerClient to reduce the load factor on the producer microservice instance, as well as to use RestTemplate to access the producer microservice method via http requests.

 

Example Application:

1. Create Eureka Server Project

2. Create a Project as a Producer microservice

  • Dependencies – Spring web, EurekaDiscoveryClient
  • Add @EnableEurekaClient on the top of main class
  • Add the following code to the application. properties
# MS Service Port number
server.port=9091

#MS  name (service name -Service Instance name)
spring.application.name=Billing-Service
# Generate  Instace id dynaically having  service name + random value
eureka.instance.instance-id=${spring.application.name}:${random.value}


#url  to register with Eureka server
eureka.client.service-url.default-zone=http://localhost:8761/eureka
  •  Create RestController having producer method
//BillingInfoController.java (Client Comp)
package com.perficient.rest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/billing/api")
public class BillingInfoController {
    @Value("${server.port}")
     private  int port;
    @Value("${eureka.instance.instance-id}")
      private  String instanceid;
    
    @GetMapping("/info")
    public  ResponseEntity<String>  fetchBillingDetails(){
       return new ResponseEntity<String>(" Final BillAmt= BillAmt- discount (Rs.5000) :: using instance::-->"+instanceid+" @port::"+port,HttpStatus.OK);   
    }

}

 

3. Create a consumer microservice project

  • Dependencies – Spring web, EurekaDiscoveryClient, open feign
  • Add @EnableEurekaClient, @EnableFeignClient annotation on the top of the main class.
package com.perficient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class MsProj04ShoppingServiceConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(MsProj04ShoppingServiceConsumerApplication.class, args);
    }

}
  • Add the following entries at application.properties
#  Server port  (acts MS port no)
server.port=7070

#MS name
spring.application.name=Shopping-Service

#url to register  with Eureka
eureka.client.service-url.default-zone=http://localhost:8761/eureka
  • Create an interface that supports Feign Client code as an in-memory dynamic proxy class.
// IBillingServiceConsumer.java
package com.perficient.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("Billing-Service")
public interface IBillingServiceConsumerClient {

   @GetMapping("/billing/api/info")
   public  ResponseEntity<String>  fetchBillingInfo();
}
  • Create a Consumer RestController and inject a FeignClient-related Proxy object to consume the destination microservice.
//ShoppingSerivceOperationsController.java (RestController)
package com.nt.rest;

import java.util.Arrays;

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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


import com.nt.client.IBillingServiceConsumerClient;

@RestController
@RequestMapping("/shopping/api")
public class ShoppingSerivceOperationsController {
     @Autowired
    private   IBillingServiceConsumerClient client;

     @GetMapping("/cart")
      public   ResponseEntity<String>  doShopping(){
         System.out.println("Proxy class name ::"+client.getClass()+" .... "+ Arrays.toString (client.getClass().getInterfaces()));
          //use  Client Comp
          String resultMsg=client.fetchBillingInfo().getBody();
        
          return  new ResponseEntity<String>("Shopping the items(shirt,trouser) :- "+resultMsg,HttpStatus.OK);
    
      }//method
      
}//class

4. Run the application in the following sequence.

  1. Run the Eureka Server application.
  2. Run the producer application multiple times but change the port number. [server.port value]
  3. Run the consumer application.

 

Stay tuned for the next tutorial, which will cover Config Server.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Bhushan Patil

Bhushan Patil is a passionate Java developer with 4+ years of experience building and maintaining high-performing web applications. He thrives in collaborative environments and possesses a strong ability to translate complex business requirements into clean, maintainable, and scalable Java code. Bhushan has a proven track record of success in designing and implementing RESTful APIs using Spring Boot and microservices and integrating them with various databases (e.g., Oracle and MongoDB). He is also well-versed in front-end technologies like JavaScript and proficient in unit testing frameworks like JUnit. He achieved this by optimizing code for performance, implementing robust security measures, and ensuring a seamless user experience. Bhushan is a continuous learner and stays updated with the latest advancements in the Java ecosystem.

More from this Author

Follow Us