Learnitweb

Implementation retry pattern in Gateway and individual microservice

Introduction

Following sample code is an example of implementing retry pattern in Gateway:

	@Bean
	public RouteLocator eshopAppRouterConfig(RouteLocatorBuilder routeLocatorBuilder) {
		return routeLocatorBuilder.routes()
				.route(p -> p.path("/eshop/productservice/**")
						.filters(f -> f.rewritePath("/eshop/productservice/(?<segment>.*)", "/${segment}")
								.circuitBreaker(config -> config.setName("productCircuitBreaker"))
								.retry(retryConfig -> retryConfig.setRetries(3).setMethods(HttpMethod.GET)
										.setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, true)))

						.uri("lb://PRODUCTSERVICE"))
				.build();
	}

Following piece of code configures a retry mechanism with a backoff strategy for HTTP requests:

.retry(retryConfig -> retryConfig.setRetries(3).setMethods(HttpMethod.GET)
    .setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, true))

Explanation

  • .retry(retryConfig -> {…})
    Configures the retry behavior for the HTTP client.
  • .setRetries(3)
    Specifies that the request will be retried up to 3 times before failing.
  • .setMethods(HttpMethod.GET)
    Defines that retries should only be applied to HTTP GET requests.
    Since GET requests are idempotent (repeating them doesn’t cause unintended side effects), they are safe for retries.
  • .setBackoff(Duration.ofMillis(100), Duration.ofMillis(1000), 2, true)
    • Configures an exponential backoff strategy for retries.
    • Initial delay: 100ms (first retry attempt will wait 100ms).
    • Max delay: 1000ms (subsequent retries will not exceed this wait time).
    • Multiplier: 2 (each retry will wait twice the previous wait time).
    • Jitter enabled (true): Randomness is introduced in the delay to avoid many requests retrying at the same time (thundering herd problem).

Implement in individual microservice

Following is an example of implementing the retry in individual microservice:

@Retry(name = "myService", fallbackMethod = "fallbackResponse")
public ResponseEntity<String> callExternalService() {
    logger.info("Calling external service...");
    throw new ConnectException("Service unavailable");
}

public ResponseEntity<String> fallbackResponse(Exception ex) {
    return ResponseEntity.ok("Fallback response: Service is down");
}

To enable retry, define a retry configuration in application.yml:

resilience4j.retry:
  instances:
    myService:
      max-attempts: 5
      wait-duration: 200ms
      exponential-backoff-multiplier: 2
      enable-randomized-wait: true
      ignore-exceptions:
        - java.lang.NullPointerException
  • max-attempts: Number of retries before failure.
  • wait-duration: Initial delay between retries.
  • exponential-backoff-multiplier: Doubles the wait time on each retry.
  • enable-randomized-wait: Introduces jitter to avoid retry storms.
  • ignore-exceptions: Specifies exceptions that should not trigger a retry.