Seeking Enhancements for RestClient Configuration

280 views Asked by At

I'm using RestClient for upstream communication and would like to add some enhancements to make it more robust. Here's my current setup:

    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    factory.setConnectTimeout(300);
    factory.setReadTimeout(300);

    RestClient restClient = RestClient.builder()
                .requestFactory(factory)
                .baseUrl("http://...:8080/..")
                .build();

    Map<String, Object> response = restClient.get()
                .uri(backend)
                .retrieve()
                .body(Map.class);

Regarding Environment:

  1. JDK21
  2. org.springframework.boot:3.2.0-M3
  3. spring.threads.virtual.enabled=true

I'd appreciate suggestions on:

  1. Integrating a circuit breaker with RestClient.
  2. Implementing a retry mechanism.
  3. Configuring the client to use a proxy gateway.

Has anyone done this before or can point me to relevant resources?

1

There are 1 answers

0
Md. Hasan Basri On BEST ANSWER

Here are the working solution I have.

/**
 * A service class responsible for processing backend requests with 
 * retry and circuit breaker mechanisms.
 */
@Service
public class Processor {

    private final RestClient restClient;
    private final ExecutorService executor;
    private final RetryTemplate retryTemplate;
    private static final String RESULT = "result";
    private static CircuitConfig circuit;

    /**
     * Constructor for the Processor class.
     *
     * @param circuit The circuit breaker configuration.
     */
    @Autowired
    public Processor(CircuitConfig circuit) {
        this.circuit = circuit;
        
        // Setting up client request factory with timeouts
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(300);
        simpleClientHttpRequestFactory.setReadTimeout(300);
        
        this.restClient = RestClient.builder()
                                    .requestFactory(simpleClientHttpRequestFactory)
                                    .baseUrl("http://...:8080/../")
                                    .build();

        this.executor = Executors.newVirtualThreadPerTaskExecutor();
        this.retryTemplate = createRetryTemplate();
        System.out.println("CompletableFuture...");
    }

    /**
     * Creates a retry template with exponential back-off policy.
     *
     * @return A configured RetryTemplate instance.
     */
    private RetryTemplate createRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        
        // Setting exponential back-off policy
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(100);
        backOffPolicy.setMultiplier(2.0);
        backOffPolicy.setMaxInterval(1000);
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // Setting simple retry policy with max attempts
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }

    /**
     * Retrieves backend response with retry mechanism.
     *
     * @param backend The backend URI.
     * @return A map representing the backend response.
     */
    public Map getBackendResp(String backend) {
        return retryTemplate.execute(retryContext ->
                restClient
                .get()
                .uri(backend)
                .retrieve()
                .body(Map.class));
    }

    /**
     * Wraps the backend request within a CompletableFuture for asynchronous processing.
     *
     * @param backend The backend URI.
     * @return A CompletableFuture containing a map representing the backend response.
     */
    private CompletableFuture<Map> getFuture(String backend) {
        return CompletableFuture.supplyAsync(() -> circuit.circuitBreaker.executeSupplier(() -> getBackendResp(backend)));
    }
}

I wanted to understand if everything operates under the virtual thread paradigm, but this aspect remains ambiguous to me.