Skip to content

Commit 89c06a4

Browse files
author
BitsAdmin
committed
Merge branch 'merged/yg/add-retryer-and-nosuchmetchod-bug' into 'integration_2025-07-17_1012625778946'
feat: [development task] core (1462280) See merge request iaasng/volcengine-java-sdk!577
2 parents 8ca0224 + 43c3e16 commit 89c06a4

15 files changed

+551
-47
lines changed

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

Lines changed: 236 additions & 25 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,13 +84,21 @@
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

66-
public class ApiClient {
101+
public class ApiClient extends BaseClient{
67102
private final static String DefaultAuthentication = "volcengineSign";
68103

69104
private boolean debugging = false;
@@ -105,6 +140,10 @@ 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+
108147
/*
109148
* Constructor for ApiClient
110149
*/
@@ -964,19 +1003,70 @@ public <T> ApiResponse<T> execute(Call call, final Type returnType, boolean... i
9641003
responseInterceptorContext.setCommon(isCommon.length > 0 && isCommon[0]);
9651004
responseInterceptorContext.setReturnType(returnType);
9661005
context.setResponseContext(responseInterceptorContext);
967-
9681006
context.setApiClient(this);
9691007

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);
1008+
int numMaxRetries = retryer.getNumMaxRetries();
1009+
ApiException apiException;
1010+
ApiResponse<T> apiResponse = null;
1011+
for (int retryCount = 0; retryCount <= numMaxRetries; retryCount++) {
1012+
apiException = null;
1013+
apiResponse = null;
1014+
try {
1015+
this.interceptorChain.executeRequest(context);
1016+
} catch (ApiException e) {
1017+
throw e;
1018+
}
1019+
1020+
try {
1021+
Call finalCall = context.getApiClient().getHttpClient().newCall(context.getRequestContext().getRequest());
1022+
Response response = finalCall.execute();
1023+
context.getResponseContext().setOriginalResponse(response);
1024+
this.interceptorChain.executeResponse(context);
1025+
apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), (T) context.getResponseContext().getData());
1026+
} catch (IOException e) {
1027+
apiException = new ApiException(e);
1028+
}catch (ApiException e) {
1029+
apiException = handleApiResponseException(e);
1030+
}
1031+
1032+
if (!requestShouldRetry(apiResponse, retryCount, apiException)) {
1033+
if (apiException != null) {
1034+
throw apiException;
1035+
}
1036+
return apiResponse;
1037+
}
1038+
}
1039+
1040+
return apiResponse;
1041+
}
1042+
1043+
private ApiException handleApiResponseException(ApiException apiException) {
1044+
if (apiException.getResponseBody() != null) {
1045+
StringBuilder builder = new StringBuilder();
1046+
Map<String, ResponseMetadata> meta = new HashMap<>();
1047+
try {
1048+
if (!convertResponseBody(apiException.getResponseBody(), builder, meta)) {
1049+
apiException.setResponseMetadata(meta.get("ResponseMetadata"));
1050+
}
1051+
}catch (Exception e) {
1052+
return apiException;
1053+
}
9791054
}
1055+
return apiException;
1056+
}
1057+
1058+
private boolean requestShouldRetry(ApiResponse apiResponse, int retryCount, ApiException lastException) throws ApiException {
1059+
if (autoRetry && retryer.shouldRetry(apiResponse, retryCount, lastException)) {
1060+
try {
1061+
long delay = retryer.getBackoffDelay(retryCount);
1062+
Thread.sleep(delay);
1063+
} catch (Exception e) {
1064+
throw new ApiException(e);
1065+
}
1066+
return true;
1067+
}
1068+
1069+
return false;
9801070
}
9811071

9821072
/**
@@ -1011,34 +1101,76 @@ public <T> void executeAsync(Call call, final Type returnType, final ApiCallback
10111101
context.setResponseContext(responseInterceptorContext);
10121102

10131103
context.setApiClient(this);
1104+
1105+
final int maxRetries = retryer.getNumMaxRetries();
1106+
final AtomicInteger retryCount = new AtomicInteger(0);
1107+
attemptAsync(context, callback, retryCount, maxRetries);
1108+
}
1109+
1110+
private <T> void attemptAsync(
1111+
final InterceptorContext context,
1112+
final ApiCallback<T> callback,
1113+
final AtomicInteger retryCount,
1114+
final int maxRetries
1115+
) {
10141116
try {
10151117
this.interceptorChain.executeRequest(context);
10161118
} catch (ApiException e) {
10171119
callback.onFailure(e, 0, null);
10181120
return;
10191121
}
1020-
Callback okHttpCallBack = new Callback() {
1122+
1123+
Call okhttpCall = getHttpClient().newCall(context.getRequestContext().getRequest());
1124+
okhttpCall.enqueue(new Callback() {
10211125
@Override
1022-
public void onFailure(Request request, IOException e) {
1023-
callback.onFailure(new ApiException(e), 0, null);
1126+
public void onFailure(Request req, IOException ioe) {
1127+
int current = retryCount.get();
1128+
ApiException apiException = new ApiException(ioe);
1129+
try {
1130+
if (autoRetry && current < maxRetries
1131+
&& requestShouldRetry(null, current, apiException)) {
1132+
retryCount.incrementAndGet();
1133+
attemptAsync(context, callback, retryCount, maxRetries);
1134+
} else {
1135+
callback.onFailure(apiException, 0, null);
1136+
}
1137+
}catch (ApiException e) {
1138+
callback.onFailure(apiException, 0, null);
1139+
}
1140+
10241141
}
10251142

10261143
@Override
10271144
public void onResponse(Response response) throws IOException {
1028-
T result;
1145+
T result = null;
1146+
ApiException apiException = null;
1147+
ApiResponse<T> apiResponse = null;
1148+
int current = retryCount.getAndIncrement();
1149+
10291150
try {
10301151
context.getResponseContext().setOriginalResponse(response);
10311152
context.getApiClient().interceptorChain.executeResponse(context);
10321153
result = (T) context.responseContext.getData();
1154+
apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), result);
10331155
} catch (ApiException e) {
1034-
callback.onFailure(e, response.code(), response.headers().toMultimap());
1035-
return;
1156+
apiException = handleApiResponseException(e);
10361157
}
1037-
callback.onSuccess(result, response.code(), response.headers().toMultimap());
1038-
}
1039-
};
10401158

1041-
((InterceptorContext) call).getRequestContext().getCall().enqueue(okHttpCallBack);
1159+
try {
1160+
if (!requestShouldRetry(apiResponse, current, apiException)) {
1161+
if (apiException != null) {
1162+
callback.onFailure(apiException, response.code(), response.headers().toMultimap());
1163+
}else {
1164+
callback.onSuccess(result, response.code(), response.headers().toMultimap());
1165+
}
1166+
}else {
1167+
attemptAsync(context, callback,retryCount , maxRetries);
1168+
}
1169+
}catch (ApiException e){
1170+
callback.onFailure(apiException, response.code(), response.headers().toMultimap());
1171+
}
1172+
}
1173+
});
10421174
}
10431175

10441176
/**
@@ -1710,4 +1842,83 @@ public ApiClient setKeepAliveDurationMs(Integer keepAliveDurationMs) {
17101842
this.httpClient.setConnectionPool(new ConnectionPool(maxIdleConns, keepAliveDurationMs));
17111843
return this;
17121844
}
1845+
1846+
public boolean isAutoRetry() {
1847+
return autoRetry;
1848+
}
1849+
1850+
public ApiClient setAutoRetry(boolean autoRetry) {
1851+
this.autoRetry = autoRetry;
1852+
return this;
1853+
}
1854+
1855+
public int getNumMaxRetries() {
1856+
return retryer.getNumMaxRetries();
1857+
}
1858+
1859+
public ApiClient setNumMaxRetries(int numMaxRetries) {
1860+
retryer.setNumMaxRetries(numMaxRetries);
1861+
return this;
1862+
}
1863+
1864+
public Set<String> getRetryErrorCodes() {
1865+
return retryer.getRetryCondition().getRetryErrorCodes();
1866+
}
1867+
1868+
public ApiClient addRetryErrorCode(String retryErrorCode) {
1869+
retryer.getRetryCondition().addRetryErrorCode(retryErrorCode);
1870+
return this;
1871+
}
1872+
1873+
public ApiClient addRetryErrorCodes(Set<String> retryErrorCodes) {
1874+
retryer.getRetryCondition().addRetryErrorCodes(retryErrorCodes);
1875+
return this;
1876+
}
1877+
1878+
public long getMinRetryDelayMs() {
1879+
return retryer.getBackoffStrategy().getMinRetryDelayMs();
1880+
}
1881+
1882+
public ApiClient setMinRetryDelayMs(long minRetryDelayMs) {
1883+
retryer.getBackoffStrategy().setMinRetryDelayMs(minRetryDelayMs);
1884+
return this;
1885+
}
1886+
1887+
public long getMaxRetryDelayMs() {
1888+
return retryer.getBackoffStrategy().getMaxRetryDelayMs();
1889+
}
1890+
1891+
public ApiClient setMaxRetryDelayMs(long maxRetryDelayMs) {
1892+
retryer.getBackoffStrategy().setMaxRetryDelayMs(maxRetryDelayMs);
1893+
return this;
1894+
}
1895+
1896+
public RetryCondition getRetryCondition() {
1897+
return retryer.getRetryCondition();
1898+
}
1899+
1900+
public ApiClient setRetryCondition(RetryCondition retryCondition) throws ApiException {
1901+
Set<String> retryErrorCodes = retryer.getRetryCondition().getRetryErrorCodes();
1902+
retryCondition.addRetryErrorCodes(retryErrorCodes);
1903+
retryer.setRetryCondition(retryCondition);
1904+
return this;
1905+
}
1906+
1907+
public BackoffStrategy getBackoffStrategy() {
1908+
return retryer.getBackoffStrategy();
1909+
}
1910+
1911+
public ApiClient setBackoffStrategy(BackoffStrategy backoffStrategy) throws ApiException {
1912+
long minRetryDelayMs = retryer.getBackoffStrategy().getMinRetryDelayMs();
1913+
long maxRetryDelayMs = retryer.getBackoffStrategy().getMaxRetryDelayMs();
1914+
backoffStrategy.setMinRetryDelayMs(minRetryDelayMs);
1915+
backoffStrategy.setMaxRetryDelayMs(maxRetryDelayMs);
1916+
retryer.setBackoffStrategy(backoffStrategy);
1917+
return this;
1918+
}
1919+
1920+
@Override
1921+
public OkHttpClient getOkHttpClient() {
1922+
return this.httpClient;
1923+
}
17131924
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.volcengine;
2+
3+
import com.squareup.okhttp.Call;
4+
import com.squareup.okhttp.OkHttpClient;
5+
import com.volcengine.interceptor.InitInterceptorContext;
6+
import com.volcengine.interceptor.InterceptorContext;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public abstract class BaseClient {
12+
public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener, boolean... isCommon) throws ApiException {
13+
InterceptorContext interceptorContext = new InterceptorContext(this.getOkHttpClient(), null);
14+
InitInterceptorContext requestInterceptorContext = new InitInterceptorContext.Builder()
15+
.path(path)
16+
.method(method)
17+
.queryParams(queryParams)
18+
.collectionQueryParams(collectionQueryParams)
19+
.body(body)
20+
.headerParams(headerParams)
21+
.formParams(formParams)
22+
.authNames(authNames)
23+
.progressRequestListener(progressRequestListener)
24+
.isCommon(false)
25+
.build();
26+
interceptorContext.setInitInterceptorContext(requestInterceptorContext);
27+
interceptorContext.setApiClient(this);
28+
return interceptorContext;
29+
}
30+
31+
public abstract OkHttpClient getOkHttpClient();
32+
33+
34+
}

volcengine-java-sdk-core/src/main/java/com/volcengine/interceptor/DeserializedResponseInterceptor.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ public InterceptorContext intercept(InterceptorContext context) throws ApiExcept
1919
Response response = context.getResponseContext().getOriginalResponse();
2020
Type returnType = context.getResponseContext().getReturnType();
2121
boolean common = context.getResponseContext().isCommon();
22-
2322
Object data = context.getApiClient().handleResponse(response, returnType, common);
2423
context.getResponseContext().setData(data);
2524
return null;

0 commit comments

Comments
 (0)