avatarVinotech

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5902

Abstract

/api/students"</span>)</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">StudentController</span> {

<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> CourseService courseService;

<span class="hljs-keyword">public</span> StudentController(CourseService courseService) {
    <span class="hljs-keyword">this</span>.courseService = courseService;
}

<span class="hljs-meta">@GetMapping(<span class="hljs-string">"/{studentId}/courses/{courseId}"</span>)</span>
<span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; getStudentCourse(<span class="hljs-meta">@PathVariable</span> String studentId, <span class="hljs-meta">@PathVariable</span> String courseId) {
    String courseDetails = courseService.getCourseDetails(courseId);
    <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"Student "</span> + studentId + <span class="hljs-string">" enrolled in "</span> + courseDetails);
}

}</pre></div><p id="51df"><b>CourseService.java</b></p><div id="84c0"><pre><span class="hljs-meta">@Service</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CourseService</span> {

<span class="hljs-keyword">private</span> final <span class="hljs-title class_">RestTemplate</span> restTemplate;

<span class="hljs-keyword">public</span> <span class="hljs-title class_">CourseService</span>(<span class="hljs-title class_">RestTemplate</span> restTemplate) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">restTemplate</span> = restTemplate;
}

<span class="hljs-meta">@Retry</span>(name = <span class="hljs-string">"courseService"</span>, fallbackMethod = <span class="hljs-string">"fallbackCourseDetails"</span>)
<span class="hljs-keyword">public</span> <span class="hljs-title class_">String</span> <span class="hljs-title function_">getCourseDetails</span>(<span class="hljs-params"><span class="hljs-built_in">String</span> courseId</span>) {
    <span class="hljs-title class_">String</span> url = <span class="hljs-string">"http://localhost:8082/api/courses/"</span> + courseId;
    <span class="hljs-keyword">return</span> restTemplate.<span class="hljs-title function_">getForObject</span>(url, <span class="hljs-title class_">String</span>.<span class="hljs-property">class</span>);
}

<span class="hljs-keyword">public</span> <span class="hljs-title class_">String</span> <span class="hljs-title function_">fallbackCourseDetails</span>(<span class="hljs-params"><span class="hljs-built_in">String</span> courseId, Throwable throwable</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Default course details"</span>;
}

}</pre></div><ul><li><b>@Retry</b>: The <code>@Retry</code> annotation in the <code>CourseService</code> class specifies that if an exception occurs during the <code>getCourseDetails()</code> method, Resilience4j will retry the method up to 3 times with a 2-second interval between attempts.</li><li><b>Fallback Method</b>: If all retry attempts fail, the fallback method <code>fallbackCourseDetails()</code> is invoked, providing a default response</li></ul><p id="889c"><b>Application.properties</b></p><div id="0a11"><pre><span class="hljs-attr">server.port</span>=<span class="hljs-number">8081</span> <span class="hljs-attr">resilience4j.retry.instances.courseService.maxAttempts</span>=<span class="hljs-number">3</span> <span class="hljs-attr">resilience4j.retry.instances.courseService.waitDuration</span>=<span class="hljs-number">2</span>s</pre></div><ol><li><b>resilience4j.retry.instances.courseService.maxAttempts=3 : </b>This property defines the <b>maximum number of retry attempts</b> that should be made for the <code>courseService</code> instance if the method fails (throws an exception).</li></ol><ul><li>In this case, when a failure occurs while trying to fetch course details, Resilience4j will <b>retry up to 3 times</b> before giving up and invoking the fallback method.</li><li>Suppose the <code>getCourseDetails()</code> method in <code>Student Service</code> calls the <code>Course Service</code>, and the <code>Course Service</code> is down or throws an error (e.g., the <code>courseId = fail</code>).</li><li>The Retry mechanism will try to call the service <b>3 times</b> (1 original attempt + 2 retries) before considering the operation as failed and invoking the fallback method.</li></ul><p id="2775">2. <b>resilience4j.retry.instances.courseService.waitDuration=2s :</b></p><ul><li>This property specifies the <b>wait duration</b> between each retry attempt.</li><li>In this case, after the first failure, Resilience4j will wait for <b>2 seconds</b> before attempting to retry the <code>courseService</code> method again.</li><li>If the <code>getCourseDetails()</code> method fails on the first attempt, the system will wait for 2 seconds before attempting the retry.</li><li>This 2-second interval ensures that there is enough time between retries, possibly allowing transient issues (e.g., a temporary network outage) to resolve themselves.</li></ul><p id="8b44"><b>Configure <code>RestTemplate</code> Bean in Student Service</b></p><div id="e8b6"><pre><span class="hljs-meta">@Configuration</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AppConfig</span> {

<span class="hljs-meta">@Bean</span>
<span class="hljs-keyword">public</span> <span class="hljs-title class_">RestTemplate</span> <span class="hljs-title function_">restTemplate</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">R

Options

estTemplate</span>(); } }</pre></div><h1 id="53c4">Postman Testing for Success and Failure Scenarios</h1><p id="e582">Now that both services (Student Service and Course Service) are set up with the Retry Pattern, you can test the success and failure scenarios using Postman.</p><h2 id="e6b5">1. Success Scenario</h2><ul><li><b>URL</b>: <code>GET <a href="http://localhost:8081/api/students/1/courses/101">http://localhost:8081/api/students/1/courses/101</a></code></li><li><b>Method</b>: GET</li></ul><p id="a517"><b>Expected Response</b>:</p><div id="f6d2"><pre><span class="hljs-string">"Student 1 enrolled in Course details for 101"</span></pre></div><p id="d5b6">This request will be successfully processed by the Course Service without any errors.</p><h2 id="e209">2. Failure Scenario with Retry and Fallback</h2><ul><li><b>URL</b>: <code>GET <a href="http://localhost:8081/api/students/1/courses/fail">http://localhost:8081/api/students/1/courses/fail</a></code></li><li><b>Method</b>: GET</li></ul><p id="acb8"><b>What Happens</b>:</p><ul><li>The Course Service throws a runtime exception (<code>Course Service is currently unavailable</code>) because the course ID is <code>"fail"</code>.</li><li>The Student Service, with the Resilience4j Retry configuration, will attempt to call the Course Service <b>3 times</b> (as specified in <code>application.properties</code>).</li><li>After the retries are exhausted, the fallback method <code>fallbackCourseDetails()</code> will be executed, providing a default response.</li></ul><p id="d88e"><b>Expected Response</b>:</p><div id="70c6"><pre><span class="hljs-string">"Student 1 enrolled in Default course details"</span></pre></div><p id="c612">This fallback response will be returned after the 3 retry attempts fail.</p><h1 id="bbee">Logs for Failure Scenario (Optional)</h1><p id="c340">If you monitor the console logs of the Student Service during the failure scenario, you should see output indicating the retries:</p><div id="8e56"><pre>Retry attempt <span class="hljs-number">1</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">courseId</span>: fail Retry attempt <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">courseId</span>: fail Retry attempt <span class="hljs-number">3</span> <span class="hljs-keyword">for</span> <span class="hljs-title class_">courseId</span>: fail Fallback method called <span class="hljs-keyword">for</span> <span class="hljs-title class_">courseId</span>: fail</pre></div><p id="3810">This ensures that Resilience4j’s Retry mechanism is functioning as expected. The retry happens automatically, and after the maximum attempts, the fallback is triggered.</p><blockquote id="cbdf"><p><i>👏<b> If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.</b></i></p></blockquote><p id="b188"><b>To read other topics</b></p><ul><li><a href="https://readmedium.com/spring-boot-circuit-breaker-example-with-resilience4j-step-by-step-guide-e4c6f82711e5"><b>Spring Boot Circuit Breaker Example with Resilience4j: Step-by-Step Guide</b></a></li><li><a href="https://readmedium.com/transactional-annotation-in-spring-data-jpa-9f803d341289"><b>Mastering Transaction Propagation and Isolation in Spring Boot</b></a></li><li><a href="https://readmedium.com/using-hibernate-formula-in-spring-boot-to-compute-derived-fields-dynamically-21006573a82c"><b>@Formula Annotation in Spring Boot</b></a></li><li><a href="https://readmedium.com/one-to-one-mapping-in-spring-boot-jpa-7b892f89a671"><b>One To One mapping in Spring Boot JPA</b></a></li><li><a href="https://readmedium.com/one-to-many-mapping-in-spring-boot-jpa-c3661451d4f2"><b>One To Many mapping in Spring Boot JPA</b></a></li><li><a href="https://readmedium.com/solid-principles-in-java-710d767aae39"><b>SOLID Principles in Java</b></a></li><li><a href="https://readmedium.com/java-8-interview-questions-and-answer-ed6fa24598ad"><b>Java 8 Interview Questions and Answer</b></a></li><li><a href="https://readmedium.com/java-string-interview-questions-and-answer-e06d0acc6c41"><b>Java String Interview Questions and answer</b></a></li><li><a href="https://readmedium.com/kafka-interview-questions-and-answers-acebe3141a96"><b>Kafka Interview Questions and Answers</b></a></li><li><a href="https://readmedium.com/optional-in-java-8-8b60f9957ccf"><b>Optional in Java 8</b></a></li><li><a href="https://readmedium.com/global-exception-handling-in-spring-boot-4f6a867f39c8"><b>Global exception handling in spring boot</b></a></li><li><a href="https://readmedium.com/pessimistic-locking-in-jpa-with-spring-boot-267fafb6f62e"><b>Pessimistic Locking in JPA with Spring Boot</b></a></li><li><a href="https://readmedium.com/optimistic-locking-in-jpa-with-spring-boot-5afe19bcedbd"><b>Optimistic Locking in JPA with Spring Boot</b></a></li><li><a href="https://readmedium.com/design-pattern-in-java-14289cc56f5b"><b>Design Pattern in java</b></a></li><li><a href="https://readmedium.com/generic-apiresponse-and-global-exception-handling-in-spring-boot-221ce807bca6"><b>Generic ApiResponse and Global Exception Handling in Spring Boot</b></a></li><li><a href="https://readmedium.com/garbage-collection-in-java-65ac29df1302"><b>Garbage Collection in Java</b></a></li><li><a href="https://readmedium.com/understanding-the-java-virtual-machine-jvm-a-comprehensive-guide-b4445a96ea30"><b>JVM Architecture</b></a></li><li><a href="https://readmedium.com/how-the-jit-compiler-enhances-java-performance-an-in-depth-explanation-f4b85797976e"><b>How the JIT Compiler Enhances Java Performance: An In-Depth Explanation</b></a></li><li><a href="https://readmedium.com/mastering-git-and-github-integration-in-intellij-idea-a-complete-guide-to-version-control-7cf68cd7951a"><b>Git and GitHub Integration in IntelliJ IDEA</b></a></li></ul></article></body>

Spring Boot Retry Pattern Example with Resilience4j: Step-by-Step Guide

The Retry Pattern in Resilience4j is a fault tolerance mechanism that allows you to automatically retry failed operations a specified number of times before giving up. This pattern is particularly useful when transient faults, such as temporary network issues or momentary service unavailability, cause the failure. The Retry Pattern helps improve the resiliency of your application by handling these intermittent failures.

The Retry Pattern is used to handle temporary failures in distributed systems or unreliable network connections. The idea is to automatically retry a failed operation a specified number of times before giving up. In Spring Boot, the Retry Pattern is often implemented using the Resilience4j or Spring Retry libraries.

Retry Pattern in Resilience4j:

  1. Configurable Retry Attempts: You can configure how many times an operation should be retried before failing completely.
  2. Wait Interval Between Retries: It allows you to specify the time delay between consecutive retry attempts.
  3. Backoff Strategy: You can implement different backoff strategies (e.g., fixed or exponential backoff) to avoid overwhelming the system during retries.
  4. Retry on Specific Exceptions: You can configure which types of exceptions should trigger retries.
  5. Result-Based Retry: It allows retries based on the result of an operation (e.g., retry only if the response is not as expected).
  6. Event Publishing: You can monitor retry attempts through events like onRetry, onSuccess, onError, and onCompletion.

To implement the Retry Pattern using Resilience4j in two Spring Boot microservices, let’s create a scenario where Service A (Student Service) communicates with Service B (Course Service). Service A will invoke an API from Service B, and if the API call fails (e.g., due to network issues or Service B being down), the Retry Pattern will attempt to re-execute the failed request a configurable number of times before finally failing.

1. Create Two Microservices

We’ll create two Spring Boot microservices:

  • Student Service: Calls the Course Service to get course details.
  • Course Service: Provides course details, which could sometimes fail or delay to simulate a network issue.

Dependencies

Both services should include the following dependencies in pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
</dependency>

2. Course Service (Service B)

This microservice will provide course details but occasionally throw an exception to simulate a failure.

CourseController.java

@RestController
@RequestMapping("/api/courses")
public class CourseController {

    @GetMapping("/{courseId}")
    public ResponseEntity<String> getCourseDetails(@PathVariable String courseId) {
        // Simulating failure on purpose for testing Retry Pattern
        if ("fail".equalsIgnoreCase(courseId)) {
            throw new RuntimeException("Course Service is currently unavailable");
        }
        return ResponseEntity.ok("Course details for " + courseId);
    }
}

Application.properties

server.port=8082

3. Student Service (Service A)

This microservice will call Course Service and use Resilience4j Retry to handle failures by retrying the API call.

StudentController.java

@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final CourseService courseService;

    public StudentController(CourseService courseService) {
        this.courseService = courseService;
    }

    @GetMapping("/{studentId}/courses/{courseId}")
    public ResponseEntity<String> getStudentCourse(@PathVariable String studentId, @PathVariable String courseId) {
        String courseDetails = courseService.getCourseDetails(courseId);
        return ResponseEntity.ok("Student " + studentId + " enrolled in " + courseDetails);
    }
}

CourseService.java

@Service
public class CourseService {

    private final RestTemplate restTemplate;

    public CourseService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Retry(name = "courseService", fallbackMethod = "fallbackCourseDetails")
    public String getCourseDetails(String courseId) {
        String url = "http://localhost:8082/api/courses/" + courseId;
        return restTemplate.getForObject(url, String.class);
    }

    public String fallbackCourseDetails(String courseId, Throwable throwable) {
        return "Default course details";
    }
}
  • @Retry: The @Retry annotation in the CourseService class specifies that if an exception occurs during the getCourseDetails() method, Resilience4j will retry the method up to 3 times with a 2-second interval between attempts.
  • Fallback Method: If all retry attempts fail, the fallback method fallbackCourseDetails() is invoked, providing a default response

Application.properties

server.port=8081
resilience4j.retry.instances.courseService.maxAttempts=3
resilience4j.retry.instances.courseService.waitDuration=2s
  1. resilience4j.retry.instances.courseService.maxAttempts=3 : This property defines the maximum number of retry attempts that should be made for the courseService instance if the method fails (throws an exception).
  • In this case, when a failure occurs while trying to fetch course details, Resilience4j will retry up to 3 times before giving up and invoking the fallback method.
  • Suppose the getCourseDetails() method in Student Service calls the Course Service, and the Course Service is down or throws an error (e.g., the courseId = fail).
  • The Retry mechanism will try to call the service 3 times (1 original attempt + 2 retries) before considering the operation as failed and invoking the fallback method.

2. resilience4j.retry.instances.courseService.waitDuration=2s :

  • This property specifies the wait duration between each retry attempt.
  • In this case, after the first failure, Resilience4j will wait for 2 seconds before attempting to retry the courseService method again.
  • If the getCourseDetails() method fails on the first attempt, the system will wait for 2 seconds before attempting the retry.
  • This 2-second interval ensures that there is enough time between retries, possibly allowing transient issues (e.g., a temporary network outage) to resolve themselves.

Configure RestTemplate Bean in Student Service

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Postman Testing for Success and Failure Scenarios

Now that both services (Student Service and Course Service) are set up with the Retry Pattern, you can test the success and failure scenarios using Postman.

1. Success Scenario

Expected Response:

"Student 1 enrolled in Course details for 101"

This request will be successfully processed by the Course Service without any errors.

2. Failure Scenario with Retry and Fallback

What Happens:

  • The Course Service throws a runtime exception (Course Service is currently unavailable) because the course ID is "fail".
  • The Student Service, with the Resilience4j Retry configuration, will attempt to call the Course Service 3 times (as specified in application.properties).
  • After the retries are exhausted, the fallback method fallbackCourseDetails() will be executed, providing a default response.

Expected Response:

"Student 1 enrolled in Default course details"

This fallback response will be returned after the 3 retry attempts fail.

Logs for Failure Scenario (Optional)

If you monitor the console logs of the Student Service during the failure scenario, you should see output indicating the retries:

Retry attempt 1 for courseId: fail
Retry attempt 2 for courseId: fail
Retry attempt 3 for courseId: fail
Fallback method called for courseId: fail

This ensures that Resilience4j’s Retry mechanism is functioning as expected. The retry happens automatically, and after the maximum attempts, the fallback is triggered.

👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.

To read other topics

Retry Pattern
Fault Tolerance
Resilience4j
Retry Spring Boot
Retry Annotation
Recommended from ReadMedium