Resilient Microservices - ganmath/learners GitHub Wiki

Circuit Breaker Pattern Microservices

Introduction

The Circuit Breaker pattern is a resilience strategy to handle faults gracefully in microservices architecture. It's particularly useful in preventing cascading failures when one service depends on another. In the e-learning domain, where services like Course Management, User Authentication, Payments, and Content Delivery interact closely, implementing the Circuit Breaker pattern ensures continuous service availability.

Use Case Scenario in E-Learning

Let's consider an e-learning system with the following services:

  • User Management Service: Handles user registration, authentication, and authorization.
  • Course Service: Manages courses, including their creation, update, and deletion.
  • Content Delivery Service: Delivers course content (e.g., videos, articles).
  • Payment Service: Handles course purchases and subscriptions.
  • Recommendation Service: Provides course recommendations based on user preferences.

In this setup, the Recommendation Service relies on the Course Service to fetch course details, and both the Recommendation and Content Delivery Services rely on the User Management Service for user information. A failure in any service could potentially cause a cascade of failures. The Circuit Breaker pattern can prevent this by isolating the failing service and providing a fallback.

Spring Cloud Circuit Breaker Implementation

Spring Cloud Circuit Breaker, based on Resilience4j, makes it easy to implement the Circuit Breaker pattern in Spring Boot applications.

Adding Dependencies

Add the following dependencies to your pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Enabling Circuit Breaker

Enable Circuit Breaker in your Spring Boot application by adding the @EnableCircuitBreaker annotation:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;

@SpringBootApplication
@EnableCircuitBreaker
public class ElearningApplication {
    public static void main(String[] args) {
        SpringApplication.run(ElearningApplication.class, args);
    }
}

Circuit Breaker Configuration

You can configure Circuit Breakers in your application.yml or application.properties file:

resilience4j:
  circuitbreaker:
    instances:
      courseService:
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3
        recordExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException

Implementing Circuit Breaker in Services

Here's an example implementation of Circuit Breaker in a service that fetches course details from the Course Service:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RecommendationService {

    @Autowired
    private CircuitBreakerFactory circuitBreakerFactory;

    @Autowired
    private RestTemplate restTemplate;

    private static final String COURSE_SERVICE_URL = "http://course-service/courses/";

    public Course getCourseDetails(String courseId) {
        return circuitBreakerFactory.create("courseService")
                .run(() -> restTemplate.getForObject(COURSE_SERVICE_URL + courseId, Course.class),
                        throwable -> fallbackCourse(courseId));
    }

    private Course fallbackCourse(String courseId) {
        // Fallback logic for Course Service
        return new Course(courseId, "Unknown Course", "Course details not available");
    }
}

Exposing Circuit Breaker Metrics

Exposing Circuit Breaker metrics can help in monitoring and tuning. You can expose them using Spring Boot Actuator. Ensure that the spring-boot-starter-actuator dependency is included and add the following to application.yml:

management:
  endpoints:
    web:
      exposure:
        include: "*"

Access the Circuit Breaker metrics at /actuator/metrics.

Complete Example

Application Structure

Here's an example structure for our Spring Boot microservices application:

e-learning
├── recommendation-service
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── elearning
│                       └── recommendation
│                           ├── RecommendationService.java
│                           └── RecommendationApplication.java
├── course-service
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── elearning
│                       └── course
│                           ├── CourseController.java
│                           └── CourseApplication.java
└── pom.xml

recommendation-service

RecommendationApplication.java
package com.elearning.recommendation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableCircuitBreaker
public class RecommendationApplication {
    public static void main(String[] args) {
        SpringApplication.run(RecommendationApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
RecommendationService.java
package com.elearning.recommendation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RecommendationService {

    @Autowired
    private CircuitBreakerFactory circuitBreakerFactory;

    @Autowired
    private RestTemplate restTemplate;

    private static final String COURSE_SERVICE_URL = "http://course-service/courses/";

    public Course getCourseDetails(String courseId) {
        return circuitBreakerFactory.create("courseService")
                .run(() -> restTemplate.getForObject(COURSE_SERVICE_URL + courseId, Course.class),
                        throwable -> fallbackCourse(courseId));
    }

    private Course fallbackCourse(String courseId) {
        // Fallback logic for Course Service
        return new Course(courseId, "Unknown Course", "Course details not available");
    }
}
Course.java
package com.elearning.recommendation;

public class Course {
    private String id;
    private String name;
    private String description;

    public Course() {}

    public Course(String id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }

    // Getters and Setters
}

course-service

CourseApplication.java
package com.elearning.course;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CourseApplication {
    public static void main(String[] args) {
        SpringApplication.run(CourseApplication.class, args);
    }
}
CourseController.java
package com.elearning.course;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CourseController {

    @GetMapping("/courses/{id}")
    public Course getCourseById(@PathVariable String id) {
        return new Course(id, "Java Programming", "Learn Java programming in-depth.");
    }
}
Course.java
package com.elearning.course;

public class Course {
    private String id;
    private String name;
    private String description;

    public Course() {}

    public Course(String id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }

    // Getters and Setters
}

Configuration (Shared)

application.yml (recommendation-service)
resilience4j:
  circuitbreaker:
    instances:
      courseService:
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3
        recordExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException

management:
  endpoints:
    web:
      exposure:
        include: "*"

Monitoring Circuit Breaker Performance

For better visualization and monitoring of the Circuit Breaker pattern, you can integrate monitoring tools like:

  • Prometheus & Grafana: Collect and visualize metrics.
  • Hystrix Dashboard (Legacy): If still using Netflix Hystrix, you can use its dashboard.

Conclusion

Implementing the Circuit Breaker pattern in your microservices application can significantly improve fault tolerance and resilience. In the e-learning domain, where different services depend on each other for delivering course content and user experience, Circuit Breakers are crucial for ensuring continuous service availability.

Further Reading

Next Steps

  1. Implement Circuit Breaker: Add Circuit Breaker logic to other services like Content Delivery and Payment.

  2. Fallback Strategies: Enhance fallback strategies with more detailed responses.

  3. Monitoring & Tuning: Continuously monitor and tune the Circuit Breaker configuration.

  4. Blog Content: Develop blog content explaining Circuit Breaker pattern in microservices architecture using this case study.

⚠️ **GitHub.com Fallback** ⚠️