Microservices using spring cloud

Microservices using spring cloud

Microservice architecture, with its great advantages and flexibilities, besides all agility support also bring huge challenges and required maintenance in-between communication with each other. Spring framework helps us to ease and over-come on all the challenges which we face with microservices with few couples of configurations. In this article, we are practically going to create microservices with all aspects which should be cover in this architecture.

We are going to use the example of a music catalogue application which is going to return a catalogue of a specific track against an id. We are going to create three services we can say the micro-services name music catalogue service, music info service and rating info service. The complete code is available in the given GitHub repository you can down and understand it, here I am going to write down step-by-step details and configurations. Note that I am using Greenwich.SR2 spring cloud version which is compatible with spring boot 2.0.

You can download the code from the above repository to run and test it by yourself. The frameworks used in the above example are spring boot, spring web, spring cloud (Netflix), open API 3.0 (swagger), Netflix feigns etc.

Creating First Microservice

Microservice is nothing but more of a behaviour term which is build to taking care of rules such as it should be autonomous, stand-alone and represent the smallest single unit of the feature. We are going to use the spring boot application and spring rest to create microservice. Our first microservice will be music catalogue service.

@RestController
@RequestMapping(URLS.MUSIC_CAT_URL)
public class MusicCatalogController {
   @Autowired
	private RestTemplate restTemplate;
  
  @RequestMapping(value = URLS.MUSIC_CAT_BY_USER_ID, method = RequestMethod.GET)     
  public MusicCatalogResponse getCatalogByUserById(@PathVariable("musicId") int musicId) 
  {  
    //TODO : LOGIC

    restTemplate.getForObject("https://localhost:8081/music/info/12", MusicDto.class);

  }
}

In microservices, we need to communicate with other microservices too, here the problem comes, we cannot hard code URLs of other services, it will be not a finite solution, especially when we need to apply load balancing, we will have no idea how many instances are going to produce by load balancer and on which port. If we deployed our microservices on the cloud we have dynamic URLs and we don't have fixed or predefined URLs.

Service Registry And Fetch Registry

Here the concept of a service registry and fetch registry comes in handy. Those concept/features are used to do service discovery. There are two types of service discovery, one is client-side service discover and another one is service side service discovery. A discovery server request to add this feature, which resolves our above hardcodes URLs problem. All services going to register themselves with discovery server and discovery server provides them URLs and port which is not our concern we discover them while asking the discovery server. For maintaining high availability each service sends a heartbeat after a specific interval of time and discovery server familiar with the live service. Register phenomena of a service to a discovery server are called service registry.

No alt text provided for this image


Client-side service discovery

In client-side service discovery, a client sends a request to discovery server with the name of service to acquire the details about service and then discovery service send details about service and client to send a request with the required operation. The client receives path, and port of that specific micro-service and sends the request. As discovery server only responsible for providing details about register client and client handle sending requests with a required operation that's why its called client-side service discovery. The concept in which a service get actual URL's details from a discovery server is called Fetch registry.

No alt text provided for this image



Server-side service discovery

In server-side discovery, a request sent by the client to the server with require the operation of request and, discovery server is going to send the request to the client after getting its registry.

In this example, we are going to use Eureka discovery server provided by Netflix which perform client-side service discovery.

If service or discovery server itself goes down

What will happen if service is down, discovery server is after certain, iterations did not receive heartbeats by service will automatically remove it from its registry. But what will happen if the actual discover server will go off, well each client maintain a cache about details of other services as well, so they can still communicate with each other. Luckily all those things are handled by the framework itself so we do not need to worry about those things.

Discovery Server

Let's create our Eureka server, create spring boot application and add that pom.xml file, we are using Finchley.SR1 spring cloud version. One more thing to keep in mind that JAXB is now removed from java standards JDK, so we need to add its dependencies.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>


	<groupId>com.eurekaserver</groupId>
	<artifactId>EurekaServer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>


	<name>EurekaServer</name>
	<description>Eureka Server</description>


	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>


	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.SR1</spring-cloud.version>
	</properties>


	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
		</dependency>
		<dependency>
			<groupId>com.sun.xml.bind</groupId>
			<artifactId>jaxb-impl</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jaxb</groupId>
			<artifactId>jaxb-runtime</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
			<version>1.1.1</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>


	<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>


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

For creating Eureka server, we just need to add @EnableEurekaServer on the main class of spring boot application

package com.myjavatechn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {


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

That's the sample as above, now when Eureka server it tries to discover and register itself as well because Eureka server itself is a service. We can avoid that by adding these configurations in application.yml

// src/main/resources/application.yml
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

the above configuration will stop eureka from doing registration its self as a client and doing its fetch registry. Now start your spring boot application, when it started successfully it will show that your eureka server is started on port 8761. This is the default port for eureka and each eureka client is automatically hit that port for the registry. Let's hit localhost:8761 and have a look eureka user interface.

No alt text provided for this image

Under the application you can see there is no instance available because no service is running yet.

Eureka Client

For making our micro-services able to register them in the Eureka server we need to make them Eureka client. we can do that by adding @EnableEurekaClient on the main class of boot.

@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages = "com.example.music.catalog.service")
public class MusicCatalogBootServiceApplication {

//main method

}

If Eureka server is running on default port 8761, annotation @EnableEurekaClient will be going to register service automatically with server. Services will be going to register with the name of UNKNOWN, so let's define the name for the service in the application.properties now.

//src/main/resources
server.port = 8081
spring.application.name = Music-info-service

We need to provide a proper name for our service so that service can register itself with that name and when we send a request to discovery server with the name we can have the credential or details for that service which can be done by spring.application.name. If Eureka server is registered on some different port as 8761 then we need to add additional configurations, we need to tell the client about the details of discovery server, so that client service can send heartbeats and make itself registered.

server.port = 8081
spring.application.name = Music-info-service
eureka.client.service-url.defaultZone = http://localhost:8762/eureka/

In the above provided GitHub link, for creating java DTO classes I used OpenApi 3.0, you can read about that in the given link below.

Fetching Services Using Rest Template

Now if we need to connect a microservice within or outside from a microservice we do not need to provide a complete path for that instead we just need to call the service with its registered name and we can connect with it.

No alt text provided for this image

As you can see our service is up with the name of MUSIC-INFO-SERVICE, let's try to connect that using RestTemplate from Music catalogue service.

@RestController
@RequestMapping(URLS.MUSIC_CAT_URL)
public class MusicCatalogController {

	@Autowired
	private RestTemplate restTemplate;

    @RequestMapping(value = URLS.MUSIC_CAT_BY_USER_ID, method = RequestMethod.GET)
	public MusicCatalogResponse getCatalogByUserById(@PathVariable("musicId") int musicId) {
		// calling a service

     MusicDto musicDto = restTemplate.getForObject ("https://MUSIC-INFO-SERVICE/music/info/" + musicId,
            MusicDto.class);

      //TODO:  Remaing code
}

If we are running multiple instances of that service (in the case of load balancing) we can also apply client-side load balancing, using @LoadBalanced annotation while creating the bean of RestTemplate.

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
	return new RestTemplate();
}

Fetching Services Using Netflix Feign

Feign declarative HTTP client developed by Netflix is a text-base declarative Http Client inspired by Retrofit, JXRS 2.0 and Web sockets. We can request using annotation in a templatized manner to the client which are very simple and easy to use. Now let's convert our example into feign client for sending the requests. Let's add these dependencies in the pom.xml file.

<!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-core -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- starter-openfeign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

Now let's make our class as a Eureka client so that it registers itself with eureka sever and also add @EnableFeignClients annotation so that our application scan all interfaces which are annotated with @FeignClient

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@ComponentScan(basePackages = "com.example.music.catalog.service")
public class MusicCatalogBootServiceApplication {

//TODO main method

}

Netflix feign use ribbon for load balancing, we need to create an interface and with annotation @FeignClient with the name of service by which it is registered with the eureka server.

@FeignClient(name = "MUSIC-INFO-SERVICE")
@RibbonClient(name = "MUSIC-INFO-SERVICE")
public interface MusicInfoAPI {
	
	@RequestMapping("/music/info/{musicId}")
	MusicDto getMusicInfoByMusicId(@PathVariable("musicId") int musicId);
}

Now we just need to auto-wired and call the method. @RibbonClient for load balancing if we run multiple instances of service in discovery server.

Fault Tolerance And Resilience

Fault tolerance means how a system tolerates faults. Resilience can define the limit of handling faults. In a microservice architecture, we can make our services as fault tolerance by running multiple instances. Netflix ribbon then uses to handle load balancing on the client-side, which used a round-robin algorithm to send requests.

Circuit Breaker

If an API taking too much time to complete its request, in another way we can say if that API is very slow, its a dangerous situation. As whenever a request generate there will be a thread create to entertain that request if that request that too much time, then multiple requests cause multiple threads which not dispose of until the request not completed and it makes the high risk of getting a shortage of resources.

A circuit breaker is used to set sort of time out feature, that if a request taking too much long circuit breaker will break, that request and send an error/exception to the client that your request service taking too much time. We can apply time out while creating a RestTemplate object as well, which is not recommended,

@Bean
	@LoadBalanced
	public RestTemplate getRestTemplate() {
		HttpComponentsClientHttpRequestFactory c =
              new HttpComponentsClientHttpRequestFactory();
		c.setConnectTimeout(3000);
		return new RestTemplate(c);
	}

It's still a problem if the frequency of requests are faster as compared to the time of time-outs. Because time-out never going to deactivate the service.

Circuit breaker comes in handy in that situation, what it does, basically detect if a microservice is responding late, if wait and stop sending requests (holds requests for a while) and if service continues responding late then circuit breaker turn it down,

When a circuit breaker, break the service it sends a fallback to the client which can be an error response page, or error page or a default message page. It fails fast, which help fallback functionality as well.

Hystrix

Hystrix by Netflix is a framework provide a circuit breaker pattern. Let's add the spring-cloud-starter-netflix-hystrix dependency in pom.xml. Then we need to add @EnableCircuitBreaker on our application class. Then we need to add @HystrixCommand to the methods which required or needs of circuit breaking. Let's add it on music catalogue service as that service call other services.

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

And in application class,

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@ComponentScan(basePackages = "com.example.music.catalog.service")
public class MusicCatalogBootServiceApplication {
//TODO: main method
}

Now, let's add Hystrix command in the method where is required. Let's add fallback,

@RequestMapping(value = URLS.MUSIC_CAT_BY_USER_ID, method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "getFallbackMusicCat")
public MusicCatalogResponse
getCatalogByUserById(@PathVariable("musicId") int musicId) {
}

public MusicCatalogResponse getFallbackMusicCat(@PathVariable("musicId") int musicId) {
		return new MusicCatalogResponse();
	}

Hystrix wrapup module into a proxy class and behind the curtain handling everything in its proxy class and if API does not respond, hystrix keep eyes closely on that and fall back method.

Conclusion

Spring cloud technology makes our life much easier and flexible if we want to create our application in a microservice architecture. As we see that how can we easily implement service registry and fetch registry using discovery server and clients by Netflix Eureka. We also observe Http client Feign provided by Netflix and for client-side load balancing, we see that how netflix Ribbon facilitates us. Then in the last, we saw that how can we overcome the issue of fault tolerance and resilience. we also explore how we apply the circuit breaker to avoid unnecessary long latencies in a request by using the netflix hytrix circuit breaker. We understand hystrix commands and fallback mechanism.

要查看或添加评论,请登录

Aqib Javed的更多文章

  • Air Traffic Coordination Algorithm

    Air Traffic Coordination Algorithm

    The closest pair of points problem is a computational geometry problem. Given n points in a metric (2D space), find a…

  • Quagmire of N Queens Problem

    Quagmire of N Queens Problem

    The N-Queens problem is a prototypical backtracking problem that involves placing N queens on an N × N chessboard in…

  • Create Smaller Docker Images For Spring Boot Using JLink

    Create Smaller Docker Images For Spring Boot Using JLink

    Containers revolutionise software development and deployment by providing unparalleled flexibility and agility…

    1 条评论
  • Graphs in Java

    Graphs in Java

    The prerequisite of this article is an Introduction To Graph. Please go through that article first before proceeding…

    4 条评论
  • Introduction To Graphs

    Introduction To Graphs

    Graphs are one of the fundamental data structures in computer science. They play a vital role in solving complex…

    2 条评论
  • Vector vs ArrayList

    Vector vs ArrayList

    Introduction Arrays are one of the fundamental components of data structures. They play a vital role in solving…

    2 条评论
  • Finding Median Of Two Sorted Arrays

    Finding Median Of Two Sorted Arrays

    As part of our series on algorithms and data structures, today we are going to solve a very interesting problem that…

  • Hashtable vs HashMap

    Hashtable vs HashMap

    Introduction One of the most frequently asked interview questions at the entry-level is about HashMaps and Hashtables:…

    1 条评论
  • Bit Operations in Java

    Bit Operations in Java

    No doubt Java is a strong language, with its great architecture, providing write once and run anywhere with object…

  • Monolithic vs. SOA vs. Microservices

    Monolithic vs. SOA vs. Microservices

    Creating a new project is always a pain and its success is always a question that how can we make it, maintain it and…

社区洞察

其他会员也浏览了