➤ How to Code a Game
➤ Array Programs in Java
➤ Java Inline Thread Creation
➤ Java Custom Exception
➤ Hibernate vs JDBC
➤ Object Relational Mapping
➤ Check Oracle DB Size
➤ Check Oracle DB Version
➤ Generation of Computers
➤ XML Pros & Cons
➤ Git Analytics & Its Uses
➤ Top Skills for Cloud Professional
➤ How to Hire Best Candidates
➤ Scrum Master Roles & Work
➤ CyberSecurity in Python
➤ Protect from Cyber-Attack
➤ Solve App Development Challenges
➤ Top Chrome Extensions for Twitch Users
➤ Mistakes That Can Ruin Your Test Metric Program
Intra-communication Between Microservices in Spring | Intra-communication of Micro Services can be implemented using Client codes. There are 3 types:-
- DiscoveryClient + RestTemplate
- LoadBalancerClient + RestTemplate
- Open Feign | Feign Client | Abstract Client
Among these three clients, Open Feign is mostly used. DiscoveryClient is outdated and LoadBalancerClient is very little used. Open Feign is built on the top of LoadBalancerClient. But to understand the workings of Open Feign we will see all these clients. Prerequisite:- Spring Cloud Netflix Eureka Server
Table of Contents
DiscoveryClient
DiscoveryClient: It is used to fetch ServiceInstance details from Eureka based on ServiceId.
- In the case of intra-communication, if one microservices wants to communicate with another microservices then DiscoveryClient supports fetching Producer microservices data from Eureka.
- It is called a Legacy (Old) client.
- It will get all instances (List), not one with a lower load factor. We should choose one manually.
- Nowadays, it is not used in real-time. We are seeing this to understand the workflow. It is the easiest process to understand the intra-communication.
Consider one Monolithic Application having an Order and Vendor Module. The order has Vendor information. We want to convert the same into a Microservices design using DiscoveryClient.
RestConsumer Code Using DiscoveryClient
URI vs URL
URI contains a protocol, IP, Port, and context port. URL contains URI and path. If we move one application from one system to another system then the URI changes but the Path remains the same.
We have to develop 3 projects:-
- Discovery Client
- Producer Application
- Consumer Application
Discovery Client
- Create a new spring starter project
SpringCloudEurekaServer
with dependencies:- Eureka Server (spring-cloud-starter-netflix-eureka-server
). - In the starter class add the annotation:-
@EnableEurekaServer
- In
application.properties
file:-
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
Producer Application
- Create a new spring starter project
SpringCloudVendorServer
with dependencies:- Spring Web, Eureka Discovery Client (spring-cloud-starter-netflix-eureka-client
). - In the starter class add the annotation:-
@EnableDiscoveryClient
- In
application.properties
:-
server.port=9090
#serviceId
spring.application.name=SpringCloudVendorServer
# Eureka location
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
# Register with Eureka (thier default value is true,
# so no need to write it explicitly)
#eureka.client.register-with-eureka=true
#eureka.client.fetch-registry=true
- Create a RestController:-
package com.knowprogram.demo.controller;
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("/vendor")
public class VendorRestController {
@GetMapping("/msg")
public ResponseEntity<String> showVendorMsg(){
return ResponseEntity.ok("From Vendor");
}
}
Consumer Application
- Create a new spring starter project
SpringCloudOrderServer
with dependencies:- Spring Web, Eureka Discovery Client same as Producer application. - In the starter class add the annotation:-
@EnableDiscoveryClient
- In
application.properties
:-
server.port=8668
#serviceId
spring.application.name=SpringCloudOrderServer
# Eureka server location
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
- RestConsumer:-
DiscoveryClient is an interface [org.springframework.cloud.client.discovery
], Implementation class: EurekaDiscoveryClient
, the object created by the container.
package com.knowprogram.demo.consumer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class VendorRestConsumer {
// Autowire the discovery client
@Autowired
DiscoveryClient discoveryClient;
// define one operation for find/bind process
public String getVendorData() {
// go to eureka with serviceId and get list of ServiceInstances
List<ServiceInstance> instances =
discoveryClient.getInstances("SpringCloudVendorServer");
// read for ServiceInstance at index#0 (as we have only one instance now)
ServiceInstance serviceInstance = instances.get(0);
// read URI and add path
String url = serviceInstance.getUri() + "/vendor/msg";
// use RestTemplate
RestTemplate rt = new RestTemplate();
// Make call and get response
ResponseEntity<String> response = rt.getForEntity(url, String.class);
// return response body
return response.getBody();
}
}
Also see:- RestTemplate in Spring Boot
- RestController:-
package com.knowprogram.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
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.knowprogram.demo.consumer.VendorRestConsumer;
@RestController
@RequestMapping("/order")
public class OrderRestController {
@Autowired
VendorRestConsumer consumer;
@GetMapping("/info")
public ResponseEntity<String> getOrderInfo() {
return ResponseEntity.ok("From Order and " + consumer.getVendorData());
}
}
Execution Order
- Run the SpringCloudEurekaServer starter class.
- Run the SpringCloudVendorServer starter class.
- Run the SpringCloudOrderServer starter class.
- Go to the Eureka server dashboard:- http://localhost:8761/. We can find the Instances currently registered with Eureka.
- Click on “Program: SpringCloudOrderServer:8668”, and it will take you to http://program:8668/actuator/info, modify it as http://program:8668/order/info. It will give the response of API “/order/info”.
Limitations of DiscoveryClient
- It is fetching all ServiceInstances from Eureka as a List. We need only one instance that has less load factor.
- The programmer has to choose one instance manually using the List#Index.
LoadBalancerClient
LoadBalancerClient is advanced to DiscoveryClient. It supports load balancing at the consumer application side (Client side). It means Consumer App(Client App) will choose one instance from Eureka which has less load factor.
Here, LoadBalancerClient is an interface, and the Implementation class is given by Spring Cloud LoadBalancer
dependency. This class object is auto-configured by Spring Cloud.
Let us use the previous example. From the previous example, the Discovery Client (SpringCloudEurekaServer), and Producer Application (SpringCloudVendorServer) will remain the same as it is. Only the Producer Application (SpringCloudOrderServer) will be updated.
Changes in the Producer application (SpringCloudOrderServer):-
- In
application.properties
add instance Id:-
eureka.instance.instance-id=${spring.application.name}:${random.value}
- Add one more dependency Spring Cloud LoadBalancer (
spring-cloud-starter-loadbalancer
). - Update VendorRestConsumer Code:-
package com.knowprogram.demo.consumer;
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 VendorRestConsumer {
// Autowire the LoadBalancerClient
@Autowired
LoadBalancerClient client;
// define one operation for find/bind process
public String getVendorData() {
// go to eureka with serviceId and get one ServiceInstances
ServiceInstance serviceInstance = client.choose("SpringCloudVendorServer");
// read URI and add path
String url = serviceInstance.getUri() + "/vendor/msg";
// use RestTemplate
RestTemplate rt = new RestTemplate();
// Make call and get response
ResponseEntity<String> response = rt.getForEntity(url, String.class);
// return response body
return response.getBody();
}
}
The LoadBalancer will ask for the InstanceId from the Eureka Server. The Eureka server will return the instanceId having less load. But if multiple instances have the same load then it will return any random instanceId.
Optional changes in the Producer application (SpringCloudVendorServer):- To know the application is running at which port, let us modify VendorRestController and console the port number.
package com.knowprogram.demo.controller;
import org.springframework.beans.factory.annotation.Value;
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("/vendor")
public class VendorRestController {
@Value("${server.port}")
private String port;
@GetMapping("/msg")
public ResponseEntity<String> showVendorMsg() {
return ResponseEntity.ok("From Vendor. Port: " + port);
}
}
How to create multiple instances of an application on the same machine? Change the Port number and run the same application again for a new instance in the local machine.
Execution Order
- Run Eureka (SpringCloudEurekaServer) Starter class [1 time]
- Run Vendor (SpringCloudVendorServer) Starter class [run 3 times, by changing port number]
- Run Order (SpringCloudOrderServer) Starter class [ run 1 time]
- Go to the Eureka server (http://localhost:8761/). It will list all instances.
- Click on Order Link http://192.168.0.6:8668/actuator/info modify as http://192.168.0.6:8668/order/info. Refresh the page multiple times. We can see the different port numbers.
From Order and From Vendor. Port: 9091
From Order and From Vendor. Port: 9090
FeignClient
- It is also called an open Feign or abstract client.
- Here no need to define any manual code.
- Code generated using LoadBalancerClient. We need to define one interface only that follows Producer RestController. When we write FeignClient code then internally LoadBalancerClient code is generated.
- This client code makes a call to Eureka and fetches ServiceInstance using LoadBalancer, ie instance which has a Less Load Factor.
- It will make an HTTP call to the Producer Application and get the response back to the consumer even.
In the previous example, in the Consumer application (SpringCloudOrderServer):-
- Add dependency:- OpenFeign (
spring-cloud-starter-openfeign
). The existing Spring Cloud LoadBalancer (spring-cloud-starter-loadbalancer) dependency is not required, so we can remove that. - At Starter: @EnableDiscoveryClient, @EnableFeignClients
- In
application.properties
:-
server.port=8668
spring.application.name=SpringCloudOrderServer
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
- Consumer code (VendorRestConsumer):-
package com.knowprogram.demo.consumer;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient("SpringCloudVendorServer") // serviceId
public interface VendorRestConsumer {
@GetMapping("/vendor/msg")
public ResponseEntity<String> showData();
}
This interface must follow the Producer RestController. The interface name and method name can be different but the method’s request mapping, return type, and parameter must be the same. It means except method name and interface name others should match.
If the Producer has 2 RestControllers and we want intra-communication from both RestContoller then we have to create 2 interfaces in consumer by following those RestController.
- Modify RestController:-
package com.knowprogram.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
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.knowprogram.demo.consumer.VendorRestConsumer;
@RestController
@RequestMapping("/order")
public class OrderRestController {
@Autowired
VendorRestConsumer consumer;
@GetMapping("/info")
public String getOrderInfo() {
System.out.println(consumer.getClass().getName());
ResponseEntity<String> responseEntity = consumer.showData();
return "From Order and " + responseEntity.getBody();
}
}
Execution Order
- Run Eureka Server
- Run Vendor Service (3 times – by changing port number)
- Run Product service
- Go to Eureka http://localhost:8761/
- Click on
SpringCloudOrderServer
. It will take you to http://program:8668/actuator/info, modify it as http://program:8668/order/info
Assume the consumer is given as:-
@FeignClient("STD-APP")
interface StudentConsumer {
@GetMapping("/std/data")
ResponseEntity<String> getData();
}
Then the generated Proxy class looks like this:-
@Component
class $1908 implements StudentConsumer {
@Autowired
LoadBalancerClient client;
public ResponseEntity<String> getData() {
ServiceInstance si = client.choose("STD-APP");
String url = si.getUri() + "/std/data";
RestTemplate rt = new RestTemplate();
ResponseEntity<String> resp = rt.getForEntity(url, String.class);
return resp;
}
}
StudentConsumer sob = new $1908();
sysout(sob.getClass().getName());
If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!