Add customizable retry for SDK http client (#2523)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -58,15 +58,20 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.hc.client5.http.HttpRequestRetryStrategy;
|
||||||
import org.apache.hc.client5.http.classic.HttpClient;
|
import org.apache.hc.client5.http.classic.HttpClient;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||||
|
import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
|
||||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||||
|
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
||||||
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
|
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
|
||||||
|
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
|
||||||
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
|
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.ssl.SSLContextBuilder;
|
import org.apache.hc.core5.ssl.SSLContextBuilder;
|
||||||
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
import org.springframework.hateoas.Link;
|
import org.springframework.hateoas.Link;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -108,6 +113,12 @@ public class HawkbitClient {
|
|||||||
log.trace("REST API call failed!", e);
|
log.trace("REST API call failed!", e);
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final HttpRequestRetryStrategy DEFAULT_HTTP_REQUEST_RETRY_STRATEGY =
|
||||||
|
new DefaultHttpRequestRetryStrategy(
|
||||||
|
Integer.getInteger("hawkbit.sdk.http.maxRetry", 3),
|
||||||
|
TimeValue.ofSeconds(Integer.getInteger("hawkbit.sdk.http.defaultRetryIntervalSec", 10)));
|
||||||
|
|
||||||
private final HawkbitServer hawkBitServer;
|
private final HawkbitServer hawkBitServer;
|
||||||
|
|
||||||
private final Encoder encoder;
|
private final Encoder encoder;
|
||||||
@@ -117,18 +128,23 @@ public class HawkbitClient {
|
|||||||
private final ErrorDecoder errorDecoder;
|
private final ErrorDecoder errorDecoder;
|
||||||
private final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn;
|
private final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn;
|
||||||
|
|
||||||
|
private final HttpRequestRetryStrategy httpRequestRetryStrategy;
|
||||||
|
|
||||||
public HawkbitClient(
|
public HawkbitClient(
|
||||||
final HawkbitServer hawkBitServer, final Encoder encoder, final Decoder decoder, final Contract contract) {
|
final HawkbitServer hawkBitServer, final Encoder encoder, final Decoder decoder, final Contract contract) {
|
||||||
this(hawkBitServer, encoder, decoder, contract, null, null);
|
this(hawkBitServer, encoder, decoder, contract, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Customizers gets default ones and could
|
|
||||||
*/
|
|
||||||
public HawkbitClient(
|
public HawkbitClient(
|
||||||
final HawkbitServer hawkBitServer, final Encoder encoder, final Decoder decoder, final Contract contract,
|
final HawkbitServer hawkBitServer, final Encoder encoder, final Decoder decoder, final Contract contract,
|
||||||
final ErrorDecoder errorDecoder,
|
final ErrorDecoder errorDecoder, final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn) {
|
||||||
final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn) {
|
this(hawkBitServer, encoder, decoder, contract, errorDecoder, requestInterceptorFn, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HawkbitClient(
|
||||||
|
final HawkbitServer hawkBitServer, final Encoder encoder, final Decoder decoder, final Contract contract,
|
||||||
|
final ErrorDecoder errorDecoder, final BiFunction<Tenant, Controller, RequestInterceptor> requestInterceptorFn,
|
||||||
|
final HttpRequestRetryStrategy httpRequestRetryStrategy) {
|
||||||
this.hawkBitServer = hawkBitServer;
|
this.hawkBitServer = hawkBitServer;
|
||||||
this.encoder = encoder;
|
this.encoder = encoder;
|
||||||
this.decoder = decoder;
|
this.decoder = decoder;
|
||||||
@@ -136,6 +152,8 @@ public class HawkbitClient {
|
|||||||
|
|
||||||
this.errorDecoder = errorDecoder == null ? DEFAULT_ERROR_DECODER : errorDecoder;
|
this.errorDecoder = errorDecoder == null ? DEFAULT_ERROR_DECODER : errorDecoder;
|
||||||
this.requestInterceptorFn = requestInterceptorFn == null ? DEFAULT_REQUEST_INTERCEPTOR_FN : requestInterceptorFn;
|
this.requestInterceptorFn = requestInterceptorFn == null ? DEFAULT_REQUEST_INTERCEPTOR_FN : requestInterceptorFn;
|
||||||
|
|
||||||
|
this.httpRequestRetryStrategy = httpRequestRetryStrategy == null ? DEFAULT_HTTP_REQUEST_RETRY_STRATEGY : httpRequestRetryStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T mgmtService(final Class<T> serviceType, final Tenant tenantProperties) {
|
public <T> T mgmtService(final Class<T> serviceType, final Tenant tenantProperties) {
|
||||||
@@ -307,7 +325,8 @@ public class HawkbitClient {
|
|||||||
: deserialize(conn.getInputStream(), method.getReturnType(), objectMapper);
|
: deserialize(conn.getInputStream(), method.getReturnType(), objectMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FeignException toFeignException(final int responseCode, final HttpURLConnection conn, final Request request) throws IOException {
|
private static FeignException toFeignException(final int responseCode, final HttpURLConnection conn, final Request request)
|
||||||
|
throws IOException {
|
||||||
if (responseCode >= 500 && responseCode < 600) {
|
if (responseCode >= 500 && responseCode < 600) {
|
||||||
// server error
|
// server error
|
||||||
return switch (responseCode) {
|
return switch (responseCode) {
|
||||||
@@ -395,19 +414,23 @@ public class HawkbitClient {
|
|||||||
final HttpClientWrapper httpClientWrapper = HTTP_CLIENTS.get(key);
|
final HttpClientWrapper httpClientWrapper = HTTP_CLIENTS.get(key);
|
||||||
HttpClient client = httpClientWrapper == null ? null : httpClientWrapper.get();
|
HttpClient client = httpClientWrapper == null ? null : httpClientWrapper.get();
|
||||||
if (client == null) { // create
|
if (client == null) { // create
|
||||||
final CloseableHttpClient newClient;
|
final HttpClientBuilder builder = HttpClients.custom().setRetryStrategy(DEFAULT_HTTP_REQUEST_RETRY_STRATEGY);
|
||||||
|
|
||||||
if (key.isHttps()) {
|
if (key.isHttps()) {
|
||||||
// mTLS could be requested
|
// mTLS could be used / setup
|
||||||
try {
|
try {
|
||||||
newClient = tlsClient(key.getClientCertificate(), key.getServerCertificates());
|
builder.setConnectionManager(
|
||||||
|
PoolingHttpClientConnectionManagerBuilder.create()
|
||||||
|
.setTlsSocketStrategy(getTlsSocketStragegy(key.getClientCertificate(), key.getServerCertificates()))
|
||||||
|
.build());
|
||||||
} catch (final RuntimeException e) {
|
} catch (final RuntimeException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
throw new IllegalStateException("Failed to create mTLS client", e);
|
throw new IllegalStateException("Failed to create mTLS client", e);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
newClient = HttpClients.createDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final CloseableHttpClient newClient = builder.build();
|
||||||
HTTP_CLIENTS.put(key, new HttpClientWrapper(key, newClient));
|
HTTP_CLIENTS.put(key, new HttpClientWrapper(key, newClient));
|
||||||
return newClient;
|
return newClient;
|
||||||
} else {
|
} else {
|
||||||
@@ -415,7 +438,8 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static CloseableHttpClient tlsClient(final Certificate clientCertificate, final X509Certificate[] serverCertificates)
|
|
||||||
|
private static TlsSocketStrategy getTlsSocketStragegy(final Certificate clientCertificate, final X509Certificate[] serverCertificates)
|
||||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, CertificateException,
|
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, CertificateException,
|
||||||
IOException {
|
IOException {
|
||||||
final SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
|
final SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
|
||||||
@@ -433,13 +457,7 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
sslContextBuilder.loadTrustMaterial(trustStore, null);
|
sslContextBuilder.loadTrustMaterial(trustStore, null);
|
||||||
}
|
}
|
||||||
return HttpClients
|
return new DefaultClientTlsStrategy(sslContextBuilder.build());
|
||||||
.custom()
|
|
||||||
.setConnectionManager(
|
|
||||||
PoolingHttpClientConnectionManagerBuilder.create()
|
|
||||||
.setTlsSocketStrategy(new DefaultClientTlsStrategy(sslContextBuilder.build()))
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
|||||||
Reference in New Issue
Block a user