2

I have a simple microservices based app. It has 3 microservices. Movie-Catalog-Service, Ratings-Data-Service, Movie-Info-Service. Movie-Catalog-Service is dependent on Ratings-Data-Service and Movie-Info-Service. I have a getUserRating() inside the UserRatingInfo servcie class which is protected by a CircuitBreaker. When I deliberately have not started the Ratings-Data-Service and hit /catalog/10 I see:

java.lang.IllegalStateException: No instances available for ratings-data-service instead of the fallback method being executed.

package com.example.discoveryserver2;

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

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServer2Application {

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

}
package com.example.moviecatalogservice2;

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

@SpringBootApplication
public class MovieCatalogService2Application {

    public static void main(String[] args) {
        SpringApplication.run(MovieCatalogService2Application.class, args);
    }
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}
package com.example.movieinfoservice2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class MovieInfoService2Application {

    public static void main(String[] args) {
        SpringApplication.run(MovieInfoService2Application.class, args);
    }
    
    @Bean
    public RestTemplate getRestTemplate() {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setConnectTimeout(3000);
        return new RestTemplate();
    }

}
package com.example.ratingsdataservice2;

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

@SpringBootApplication
public class RatingsDataService2Application {

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

}
package com.example.moviecatalogservice2.resources;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.moviecatalogservice2.models.CatalogItem;
import com.example.moviecatalogservice2.models.UserRating;
import com.example.moviecatalogservice2.services.MovieInfo;
import com.example.moviecatalogservice2.services.UserRatingInfo;

@RestController
@RequestMapping("/catalog")
public class CatalogResource {
    
    @Autowired
    private MovieInfo movieInfo;
    
    @Autowired
    private UserRatingInfo userRatingInfo;

    @RequestMapping("/{userId}")
    public List<CatalogItem> getCatalog(@PathVariable("userId") String userId) {

        UserRating userRating = userRatingInfo.getUserRating(userId);

        return userRating.getRatings().stream()
                .map(rating -> {
                    return movieInfo.getCatalogItem(rating);
                })
                .collect(Collectors.toList());
    }
    
}
package com.example.moviecatalogservice2.services;

import java.util.Arrays;

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

import com.example.moviecatalogservice2.models.Rating;
import com.example.moviecatalogservice2.models.UserRating;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;


@Service
public class UserRatingInfo {
    
    @Autowired
    private RestTemplate restTemplate;

    @CircuitBreaker(name = "ratings-data-service-cb", fallbackMethod = "getFallbackUserRating")
    public UserRating getUserRating(String userId) {
        UserRating userRating = restTemplate.getForObject("http://ratings-data-service/ratingsdata/user/{userId}", UserRating.class, userId);
        return userRating;
    }
    
    public UserRating getFallbackUserRating(String userId, Exception exc) {
        UserRating userRating =  new UserRating();
        userRating.setUserId(userId);
        userRating.setRatings(Arrays.asList(new Rating("100", 0)));
        return userRating;
    }
    
}

pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>movie-catalog-service-2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>movie-catalog-service-2</name>
    <description>Demo spring project for practicing Resilience4j</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>21</java.version>
        <spring-cloud.version>2024.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </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.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.properties:

spring.application.name=movie-catalog-service

server.port = 8081

# This property allows us to specify the hostname that the service registers with Eureka.
eureka.instance.hostname = localhost

# Circuit Breaker for movie-info-service-cb
resilience4j.circuitbreaker.instances.movie-info-service-cb.sliding-window-type=count_based
resilience4j.circuitbreaker.instances.movie-info-service-cb.sliding-window-size=15
resilience4j.circuitbreaker.instances.movie-info-service-cb.minimum-number-of-calls=10
resilience4j.circuitbreaker.instances.movie-info-service-cb.failure-rate-threshold=50
resilience4j.circuitbreaker.instances.movie-info-service-cb.slow-call-rate-threshold=90
resilience4j.circuitbreaker.instances.movie-info-service-cb.slow-call-duration-threshold=4000
resilience4j.circuitbreaker.instances.movie-info-service-cb.wait-duration-in-open-state=20000
resilience4j.circuitbreaker.instances.movie-info-service-cb.max-wait-duration-in-half-open-state=6000
resilience4j.circuitbreaker.instances.movie-info-service-cb.permitted-number-of-calls-in-half-open-state=3

# Circuit Breaker for ratings-data-service-cb
resilience4j.circuitbreaker.instances.ratings-data-service-cb.sliding-window-type=time_based
resilience4j.circuitbreaker.instances.ratings-data-service-cb.sliding-window-size=60
resilience4j.circuitbreaker.instances.ratings-data-service-cb.minimum-number-of-calls=10
resilience4j.circuitbreaker.instances.ratings-data-service-cb.failure-rate-threshold=40
resilience4j.circuitbreaker.instances.ratings-data-service-cb.slow-call-rate-threshold=60
resilience4j.circuitbreaker.instances.ratings-data-service-cb.slow-call-duration-threshold=2000
resilience4j.circuitbreaker.instances.ratings-data-service-cb.wait-duration-in-open-state=10000
resilience4j.circuitbreaker.instances.ratings-data-service-cb.max-wait-duration-in-half-open-state=6000
resilience4j.circuitbreaker.instances.ratings-data-service-cb.permitted-number-of-calls-in-half-open-state=3
1

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.