0

I have RestTemplate configured to communicate with two services, but as it is maintenance mode, can we use RestTemplate in Spring Boot 3?

One of the implementations, an end point which is configured through RestTemplate, is not working I am unable to communicate between the services.

Here I am getting this error:

java.lang.IllegalArgumentException: Service Instance cannot be null
    at org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient.execute(BlockingLoadBalancerClient.java:98)

Many blogs suggested using WebClient instead of RestTemplate.

This is my configuration class. When I remove this @Loadbalanced annotation, the RestTemplate operation works fine, but for my case, I need to have @Loadbalanced as it will divert my traffic to instances. Please suggest me any solution to tackle this.

@Configuration
@Slf4j
@Data

public class RestTemplateConfigLoadBalanced {

    private static final int FIX_DELAY = 10000;
    private final RestTemplateConfigProperties loadBalancedProperties;

    @Value("${discovery.port:8080}")
    private Integer discoveryPort;

    @LoadBalanced
    @Bean(name = "loadBalancedClient")
    public RestTemplate restTemplate(OutboundRequestIdAppender outboundRequestIdAppender,
                                     RestTemplateRequestLogger outboundRequestLogger) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory1());

        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        interceptors.add(outboundRequestIdAppender);
        interceptors.add(outboundRequestLogger);
        restTemplate.setInterceptors(interceptors);

        return restTemplate;
    }

    @Bean
    public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory1() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setHttpClient(httpClient1());

        return clientHttpRequestFactory;
    }

    @Bean
    public CloseableHttpClient httpClient1() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        org.apache.hc.client5.http.config.RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(Timeout.ofDays(loadBalancedProperties.getRequestTimeout()))
                .setConnectionRequestTimeout(Timeout.ofDays(loadBalancedProperties.getConnectTimeout()))
                .setResponseTimeout(Timeout.ofDays(loadBalancedProperties.getSocketTimeout()))
                .build();

        SSLContext sslContext = SSLContexts.custom()
                .loadTrustMaterial(new TrustSelfSignedStrategy())
                .build();

        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, org.apache.hc.client5.http.ssl.NoopHostnameVerifier.INSTANCE);

        Duration evictIdleDuration = Duration.of(loadBalancedProperties.getEvictIdleConnectionsIdleTime(), ChronoUnit.DAYS);
        long evictIdleMilliseconds = evictIdleDuration.toMillis();


        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);


        HttpRequestRetryStrategy customRetryStrategy = new HttpRequestRetryStrategy() {
            @Override
            public boolean retryRequest(HttpRequest httpRequest, IOException e, int executionCount, HttpContext httpContext) {
                return retryHttpRequest(executionCount);
            }

            @Override
            public boolean retryRequest(HttpResponse httpResponse, int i, HttpContext httpContext) {
                return false;
            }

            @Override
            public TimeValue getRetryInterval(HttpResponse httpResponse, int i, HttpContext httpContext) {
                return null;
            }
        };

        return HttpClients.custom()
                .setSchemePortResolver(new SchemePortResolver() {
                    @Override
                    public int resolve(org.apache.hc.core5.http.HttpHost httpHost) {
                        return discoveryPort;
                    }

                })
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connectionManager)
                .evictIdleConnections(TimeValue.ofMilliseconds(evictIdleMilliseconds))
                .evictExpiredConnections()
                .setKeepAliveStrategy(connectionKeepAliveStrategy1())
//                .setRetryStrategy((exception, executionCount, context) -> retryHttpRequest(executionCount))
                .setRetryStrategy(customRetryStrategy)
                .build();
    }

    @Bean(name = "loadbalancePoolingConnectionManager")
    public org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager poolingConnectionManager1() {
        return getPoolingHttpClientConnectionManager(log, loadBalancedProperties.getMaxTotalConnections(), loadBalancedProperties.getMaxRoutePerHost());
    }

    @Bean
    public ConnectionKeepAliveStrategy connectionKeepAliveStrategy1() {
        return (response, context) -> TimeValue.ofDays(determineConnectionKeepAliveTime(response));
    }

    @Bean
    public Runnable idleConnectionMonitor(@Qualifier("loadbalancePoolingConnectionManager") final PoolingHttpClientConnectionManager connectionManager) {
        return getRunnable(connectionManager, log, loadBalancedProperties.getCloseIdleConnectionWaitTimeSecs());
    }

    public long determineConnectionKeepAliveTime(HttpResponse response) {
        return getConnectionKeepAliveTime(response, loadBalancedProperties.getDefaultKeepAliveTimeMillis());
    }
    static long getConnectionKeepAliveTime(HttpResponse response, int defaultKeepAliveTimeMillis) {
        BasicHeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));

        while (it.hasNext()) {
            HeaderElement he = it.next();
            String param = he.getName();
            String value = he.getValue();

            if (value != null && param.equalsIgnoreCase("timeout")) {
                return Long.parseLong(value) * 1000;
            }
        }
        return defaultKeepAliveTimeMillis;
    }
    static Runnable getRunnable(PoolingHttpClientConnectionManager connectionManager, Logger log, int closeIdleConnectionWaitTimeSecs) {
        return new Runnable() {

            @Override
            @Scheduled(fixedDelay = FIX_DELAY)
            public void run() {
                if (connectionManager != null) {
                    log.trace("run IdleConnectionMonitor - Closing expired and idle connections...");
                    connectionManager.closeExpired();
                    connectionManager.closeIdle(TimeValue.ofDays(closeIdleConnectionWaitTimeSecs));
                } else {
                    log.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
                }
            }
        };
    }
    static PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager(Logger log, int maxTotalConnections, int maxRoutePerHost) {
        SSLContextBuilder builder = new SSLContextBuilder();
        try {
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
        } catch (NoSuchAlgorithmException | KeyStoreException e) {
            log.warn("Exception occurred while building SSL context", e);
        }

        SSLConnectionSocketFactory sslsf = null;
        try {
            sslsf = new SSLConnectionSocketFactory(builder.build());
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            log.warn("Exception occurred while creating SSL connection socket factory", e);
        }

        assert sslsf != null;
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                .<ConnectionSocketFactory>create().register("https", sslsf)
                .register("http", new PlainConnectionSocketFactory())
                .build();

        PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        poolingConnectionManager.setMaxTotal(maxTotalConnections);
        poolingConnectionManager.setDefaultMaxPerRoute(maxRoutePerHost);

        return poolingConnectionManager;
    }
    public boolean retryHttpRequest(int executionCount) {
        if (executionCount > loadBalancedProperties.getMaxExecutionCount()) {
            log.warn("Maximum retries {} reached", loadBalancedProperties.getMaxExecutionCount());
            return false;
        }

        log.warn("Retry http request. Retry Count: {}", executionCount);
        return true;
    }

}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • There is no issue with `RestTemplate` and Spring Boot 3 and you don't have to switch to `WebClient`. According to the exception, you're using some kind of client-side load balancing or service discovery (Eureka?) and that configuration is failing. Please include the relevant information for us to reproduce the issue. – g00glen00b Aug 30 '23 at 07:03
  • You are upgrading Spring Boot, but are also using Spring Cloud. Make sure you are using a compatible version of Spring Cloud as that is tied to certain versions of Spring Boot. – M. Deinum Aug 30 '23 at 07:04
  • It is working fine for WebClient but not for RestTemplate if you have any suggestions how it will work for RestTemplate so please drop it. @g00glen00b – Suchit Khadtar Aug 30 '23 at 10:39
  • @SuchitKhadtar Like I said, please include the relevant information. For example: which Spring Cloud dependencies are you using? How are you configuring your `RestTemplate`? How are you configuring the Spring Cloud load balancer? – g00glen00b Aug 30 '23 at 11:29
  • @g00glen00b this is my restTemplateConfig and i am using spring cloud version 2022.0.3 – Suchit Khadtar Aug 31 '23 at 06:08
  • Which service registry are you using? Your own? Eureka? Can you verify that the service instance that you're trying to use is actually available? – g00glen00b Aug 31 '23 at 06:27
  • @g00glen00b I have deployed my service in EKS(Elastic Kubernetes service) – Suchit Khadtar Aug 31 '23 at 06:37

0 Answers0