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;
}
}