Skip to content

Commit 54246cb

Browse files
author
yuguo.dtpe
committed
feat: add retryer
1 parent ff7595c commit 54246cb

14 files changed

+643
-39
lines changed

volcengine-java-sdk-core/src/main/java/com/volcengine/ApiClient.java

Lines changed: 236 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,38 @@
1414

1515
import com.google.gson.annotations.SerializedName;
1616
import com.google.gson.reflect.TypeToken;
17-
import com.squareup.okhttp.*;
17+
import com.squareup.okhttp.Call;
18+
import com.squareup.okhttp.Callback;
19+
import com.squareup.okhttp.ConnectionPool;
20+
import com.squareup.okhttp.FormEncodingBuilder;
21+
import com.squareup.okhttp.Headers;
22+
import com.squareup.okhttp.MediaType;
23+
import com.squareup.okhttp.MultipartBuilder;
24+
import com.squareup.okhttp.OkHttpClient;
25+
import com.squareup.okhttp.Request;
26+
import com.squareup.okhttp.RequestBody;
27+
import com.squareup.okhttp.Response;
1828
import com.squareup.okhttp.internal.http.HttpMethod;
1929
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
2030
import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level;
2131
import com.volcengine.auth.Authentication;
2232
import com.volcengine.auth.CredentialProvider;
2333
import com.volcengine.endpoint.DefaultEndpointProvider;
2434
import com.volcengine.endpoint.EndpointResolver;
25-
import com.volcengine.interceptor.*;
35+
import com.volcengine.interceptor.BuildRequestInterceptor;
36+
import com.volcengine.interceptor.DeserializedResponseInterceptor;
37+
import com.volcengine.interceptor.InitInterceptorContext;
38+
import com.volcengine.interceptor.InterceptorChain;
39+
import com.volcengine.interceptor.InterceptorContext;
40+
import com.volcengine.interceptor.ResolveEndpointInterceptor;
41+
import com.volcengine.interceptor.ResponseInterceptorContext;
42+
import com.volcengine.interceptor.SignRequestInterceptor;
2643
import com.volcengine.model.AbstractResponse;
2744
import com.volcengine.model.ResponseMetadata;
45+
import com.volcengine.retryer.BackoffStrategy;
46+
import com.volcengine.retryer.DefaultRetryerSetting;
47+
import com.volcengine.retryer.RetryCondition;
48+
import com.volcengine.retryer.Retryer;
2849
import com.volcengine.sign.Credentials;
2950
import com.volcengine.sign.ServiceInfo;
3051
import com.volcengine.sign.VolcstackSign;
@@ -37,7 +58,13 @@
3758
import org.threeten.bp.OffsetDateTime;
3859
import org.threeten.bp.format.DateTimeFormatter;
3960

40-
import javax.net.ssl.*;
61+
import javax.net.ssl.HostnameVerifier;
62+
import javax.net.ssl.KeyManager;
63+
import javax.net.ssl.SSLContext;
64+
import javax.net.ssl.SSLSession;
65+
import javax.net.ssl.TrustManager;
66+
import javax.net.ssl.TrustManagerFactory;
67+
import javax.net.ssl.X509TrustManager;
4168
import java.io.File;
4269
import java.io.IOException;
4370
import java.io.InputStream;
@@ -57,9 +84,17 @@
5784
import java.security.cert.CertificateFactory;
5885
import java.security.cert.X509Certificate;
5986
import java.text.DateFormat;
60-
import java.util.*;
87+
import java.util.ArrayList;
88+
import java.util.Collection;
89+
import java.util.Collections;
90+
import java.util.Date;
91+
import java.util.HashMap;
92+
import java.util.List;
93+
import java.util.Map;
6194
import java.util.Map.Entry;
95+
import java.util.Set;
6296
import java.util.concurrent.TimeUnit;
97+
import java.util.concurrent.atomic.AtomicInteger;
6398
import java.util.regex.Matcher;
6499
import java.util.regex.Pattern;
65100

@@ -105,6 +140,12 @@ public class ApiClient {
105140

106141
private Boolean useDualStack;
107142

143+
private boolean autoRetry = DefaultRetryerSetting.DEFAULT_AUTO_RETRY_ENABLED;
144+
145+
private final Retryer retryer = DefaultRetryerSetting.DEFAULT_RETRYER;
146+
147+
private static final SdkLogger LOGGER = SdkLogger.getLogger(ApiClient.class);
148+
108149
/*
109150
* Constructor for ApiClient
110151
*/
@@ -133,6 +174,7 @@ public ApiClient() {
133174
interceptorChain.appendRequestInterceptor(new SignRequestInterceptor());
134175

135176
interceptorChain.appendResponseInterceptor(new DeserializedResponseInterceptor());
177+
SdkLogger.enableConsoleOutput(java.util.logging.Level.WARNING);
136178
}
137179

138180
/**
@@ -434,9 +476,11 @@ public ApiClient setDebugging(boolean debugging) {
434476
loggingInterceptor = new HttpLoggingInterceptor();
435477
loggingInterceptor.setLevel(Level.BODY);
436478
httpClient.interceptors().add(loggingInterceptor);
479+
SdkLogger.enableConsoleOutput(java.util.logging.Level.ALL);
437480
} else {
438481
httpClient.interceptors().remove(loggingInterceptor);
439482
loggingInterceptor = null;
483+
SdkLogger.enableConsoleOutput(java.util.logging.Level.WARNING);
440484
}
441485
}
442486
this.debugging = debugging;
@@ -964,19 +1008,71 @@ public <T> ApiResponse<T> execute(Call call, final Type returnType, boolean... i
9641008
responseInterceptorContext.setCommon(isCommon.length > 0 && isCommon[0]);
9651009
responseInterceptorContext.setReturnType(returnType);
9661010
context.setResponseContext(responseInterceptorContext);
967-
9681011
context.setApiClient(this);
9691012

970-
try {
971-
this.interceptorChain.executeRequest(context);
972-
Call finalCall = context.requestContext.getCall();
973-
Response response = finalCall.execute();
974-
context.getResponseContext().setOriginalResponse(response);
975-
this.interceptorChain.executeResponse(context);
976-
return new ApiResponse<T>(response.code(), response.headers().toMultimap(), (T) context.getResponseContext().getData());
977-
} catch (IOException e) {
978-
throw new ApiException(e);
1013+
int numMaxRetries = retryer.getNumMaxRetries();
1014+
ApiException apiException;
1015+
ApiResponse<T> apiResponse = null;
1016+
for (int retryCount = 0; retryCount <= numMaxRetries; retryCount++) {
1017+
apiException = null;
1018+
apiResponse = null;
1019+
try {
1020+
this.interceptorChain.executeRequest(context);
1021+
} catch (ApiException e) {
1022+
throw e;
1023+
}
1024+
1025+
try {
1026+
Call finalCall = context.getApiClient().getHttpClient().newCall(context.getRequestContext().getRequest());
1027+
Response response = finalCall.execute();
1028+
context.getResponseContext().setOriginalResponse(response);
1029+
this.interceptorChain.executeResponse(context);
1030+
apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), (T) context.getResponseContext().getData());
1031+
} catch (IOException e) {
1032+
apiException = new ApiException(e);
1033+
}catch (ApiException e) {
1034+
apiException = handleApiResponseException(e);
1035+
}
1036+
1037+
if (!requestShouldRetry(apiResponse, retryCount, apiException)) {
1038+
if (apiException != null) {
1039+
throw apiException;
1040+
}
1041+
return apiResponse;
1042+
}
1043+
}
1044+
1045+
return apiResponse;
1046+
}
1047+
1048+
private ApiException handleApiResponseException(ApiException apiException) {
1049+
if (apiException.getResponseBody() != null) {
1050+
StringBuilder builder = new StringBuilder();
1051+
Map<String, ResponseMetadata> meta = new HashMap<>();
1052+
try {
1053+
if (!convertResponseBody(apiException.getResponseBody(), builder, meta)) {
1054+
apiException.setResponseMetadata(meta.get("ResponseMetadata"));
1055+
}
1056+
}catch (Exception e) {
1057+
return apiException;
1058+
}
9791059
}
1060+
return apiException;
1061+
}
1062+
1063+
private boolean requestShouldRetry(ApiResponse apiResponse, int retryCount, ApiException lastException) throws ApiException {
1064+
if (autoRetry && retryer.shouldRetry(apiResponse, retryCount, lastException)) {
1065+
try {
1066+
long delay = retryer.getBackoffDelay(retryCount);
1067+
LOGGER.debug("retryCount: " + (retryCount + 1) + ", delay: " + delay + ", backoffStrategy: " + retryer.getBackoffStrategy().getClass().getName());
1068+
Thread.sleep(delay);
1069+
} catch (Exception e) {
1070+
throw new ApiException(e);
1071+
}
1072+
return true;
1073+
}
1074+
1075+
return false;
9801076
}
9811077

9821078
/**
@@ -1011,34 +1107,76 @@ public <T> void executeAsync(Call call, final Type returnType, final ApiCallback
10111107
context.setResponseContext(responseInterceptorContext);
10121108

10131109
context.setApiClient(this);
1110+
1111+
final int maxRetries = retryer.getNumMaxRetries();
1112+
final AtomicInteger retryCount = new AtomicInteger(0);
1113+
attemptAsync(context, callback, retryCount, maxRetries);
1114+
}
1115+
1116+
private <T> void attemptAsync(
1117+
final InterceptorContext context,
1118+
final ApiCallback<T> callback,
1119+
final AtomicInteger retryCount,
1120+
final int maxRetries
1121+
) {
10141122
try {
10151123
this.interceptorChain.executeRequest(context);
10161124
} catch (ApiException e) {
10171125
callback.onFailure(e, 0, null);
10181126
return;
10191127
}
1020-
Callback okHttpCallBack = new Callback() {
1128+
1129+
Call okhttpCall = getHttpClient().newCall(context.getRequestContext().getRequest());
1130+
okhttpCall.enqueue(new Callback() {
10211131
@Override
1022-
public void onFailure(Request request, IOException e) {
1023-
callback.onFailure(new ApiException(e), 0, null);
1132+
public void onFailure(Request req, IOException ioe) {
1133+
int current = retryCount.get();
1134+
ApiException apiException = new ApiException(ioe);
1135+
try {
1136+
if (autoRetry && current < maxRetries
1137+
&& requestShouldRetry(null, current, apiException)) {
1138+
retryCount.incrementAndGet();
1139+
attemptAsync(context, callback, retryCount, maxRetries);
1140+
} else {
1141+
callback.onFailure(apiException, 0, null);
1142+
}
1143+
}catch (ApiException e) {
1144+
callback.onFailure(apiException, 0, null);
1145+
}
1146+
10241147
}
10251148

10261149
@Override
10271150
public void onResponse(Response response) throws IOException {
1028-
T result;
1151+
T result = null;
1152+
ApiException apiException = null;
1153+
ApiResponse<T> apiResponse = null;
1154+
int current = retryCount.getAndIncrement();
1155+
10291156
try {
10301157
context.getResponseContext().setOriginalResponse(response);
10311158
context.getApiClient().interceptorChain.executeResponse(context);
10321159
result = (T) context.responseContext.getData();
1160+
apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), result);
10331161
} catch (ApiException e) {
1034-
callback.onFailure(e, response.code(), response.headers().toMultimap());
1035-
return;
1162+
apiException = handleApiResponseException(e);
10361163
}
1037-
callback.onSuccess(result, response.code(), response.headers().toMultimap());
1038-
}
1039-
};
10401164

1041-
((InterceptorContext) call).getRequestContext().getCall().enqueue(okHttpCallBack);
1165+
try {
1166+
if (!requestShouldRetry(apiResponse, current, apiException)) {
1167+
if (apiException != null) {
1168+
callback.onFailure(apiException, response.code(), response.headers().toMultimap());
1169+
}else {
1170+
callback.onSuccess(result, response.code(), response.headers().toMultimap());
1171+
}
1172+
}else {
1173+
attemptAsync(context, callback,retryCount , maxRetries);
1174+
}
1175+
}catch (ApiException e){
1176+
callback.onFailure(apiException, response.code(), response.headers().toMultimap());
1177+
}
1178+
}
1179+
});
10421180
}
10431181

10441182
/**
@@ -1710,4 +1848,78 @@ public ApiClient setKeepAliveDurationMs(Integer keepAliveDurationMs) {
17101848
this.httpClient.setConnectionPool(new ConnectionPool(maxIdleConns, keepAliveDurationMs));
17111849
return this;
17121850
}
1851+
1852+
public boolean isAutoRetry() {
1853+
return autoRetry;
1854+
}
1855+
1856+
public ApiClient setAutoRetry(boolean autoRetry) {
1857+
this.autoRetry = autoRetry;
1858+
return this;
1859+
}
1860+
1861+
public int getNumMaxRetries() {
1862+
return retryer.getNumMaxRetries();
1863+
}
1864+
1865+
public ApiClient setNumMaxRetries(int numMaxRetries) {
1866+
retryer.setNumMaxRetries(numMaxRetries);
1867+
return this;
1868+
}
1869+
1870+
public Set<String> getRetryErrorCodes() {
1871+
return retryer.getRetryCondition().getRetryErrorCodes();
1872+
}
1873+
1874+
public ApiClient addRetryErrorCode(String retryErrorCode) {
1875+
retryer.getRetryCondition().addRetryErrorCode(retryErrorCode);
1876+
return this;
1877+
}
1878+
1879+
public ApiClient addRetryErrorCodes(Set<String> retryErrorCodes) {
1880+
retryer.getRetryCondition().addRetryErrorCodes(retryErrorCodes);
1881+
return this;
1882+
}
1883+
1884+
public long getMinRetryDelayMs() {
1885+
return retryer.getBackoffStrategy().getMinRetryDelayMs();
1886+
}
1887+
1888+
public ApiClient setMinRetryDelayMs(long minRetryDelayMs) {
1889+
retryer.getBackoffStrategy().setMinRetryDelayMs(minRetryDelayMs);
1890+
return this;
1891+
}
1892+
1893+
public long getMaxRetryDelayMs() {
1894+
return retryer.getBackoffStrategy().getMaxRetryDelayMs();
1895+
}
1896+
1897+
public ApiClient setMaxRetryDelayMs(long maxRetryDelayMs) {
1898+
retryer.getBackoffStrategy().setMaxRetryDelayMs(maxRetryDelayMs);
1899+
return this;
1900+
}
1901+
1902+
public RetryCondition getRetryCondition() {
1903+
return retryer.getRetryCondition();
1904+
}
1905+
1906+
public ApiClient setRetryCondition(RetryCondition retryCondition) throws ApiException {
1907+
Set<String> retryErrorCodes = retryer.getRetryCondition().getRetryErrorCodes();
1908+
retryCondition.addRetryErrorCodes(retryErrorCodes);
1909+
retryer.setRetryCondition(retryCondition);
1910+
return this;
1911+
}
1912+
1913+
public BackoffStrategy getBackoffStrategy() {
1914+
return retryer.getBackoffStrategy();
1915+
}
1916+
1917+
public ApiClient setBackoffStrategy(BackoffStrategy backoffStrategy) throws ApiException {
1918+
long minRetryDelayMs = retryer.getBackoffStrategy().getMinRetryDelayMs();
1919+
long maxRetryDelayMs = retryer.getBackoffStrategy().getMaxRetryDelayMs();
1920+
backoffStrategy.setMinRetryDelayMs(minRetryDelayMs);
1921+
backoffStrategy.setMaxRetryDelayMs(maxRetryDelayMs);
1922+
retryer.setBackoffStrategy(backoffStrategy);
1923+
return this;
1924+
}
17131925
}

0 commit comments

Comments
 (0)