|
14 | 14 |
|
15 | 15 | import com.google.gson.annotations.SerializedName; |
16 | 16 | 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; |
18 | 28 | import com.squareup.okhttp.internal.http.HttpMethod; |
19 | 29 | import com.squareup.okhttp.logging.HttpLoggingInterceptor; |
20 | 30 | import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level; |
21 | 31 | import com.volcengine.auth.Authentication; |
22 | 32 | import com.volcengine.auth.CredentialProvider; |
23 | 33 | import com.volcengine.endpoint.DefaultEndpointProvider; |
24 | 34 | 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; |
26 | 43 | import com.volcengine.model.AbstractResponse; |
27 | 44 | 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; |
28 | 49 | import com.volcengine.sign.Credentials; |
29 | 50 | import com.volcengine.sign.ServiceInfo; |
30 | 51 | import com.volcengine.sign.VolcstackSign; |
|
37 | 58 | import org.threeten.bp.OffsetDateTime; |
38 | 59 | import org.threeten.bp.format.DateTimeFormatter; |
39 | 60 |
|
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; |
41 | 68 | import java.io.File; |
42 | 69 | import java.io.IOException; |
43 | 70 | import java.io.InputStream; |
|
57 | 84 | import java.security.cert.CertificateFactory; |
58 | 85 | import java.security.cert.X509Certificate; |
59 | 86 | 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; |
61 | 94 | import java.util.Map.Entry; |
| 95 | +import java.util.Set; |
62 | 96 | import java.util.concurrent.TimeUnit; |
| 97 | +import java.util.concurrent.atomic.AtomicInteger; |
63 | 98 | import java.util.regex.Matcher; |
64 | 99 | import java.util.regex.Pattern; |
65 | 100 |
|
66 | | -public class ApiClient { |
| 101 | +public class ApiClient extends BaseClient{ |
67 | 102 | private final static String DefaultAuthentication = "volcengineSign"; |
68 | 103 |
|
69 | 104 | private boolean debugging = false; |
@@ -105,6 +140,10 @@ public class ApiClient { |
105 | 140 |
|
106 | 141 | private Boolean useDualStack; |
107 | 142 |
|
| 143 | + private boolean autoRetry = DefaultRetryerSetting.DEFAULT_AUTO_RETRY_ENABLED; |
| 144 | + |
| 145 | + private final Retryer retryer = DefaultRetryerSetting.DEFAULT_RETRYER; |
| 146 | + |
108 | 147 | /* |
109 | 148 | * Constructor for ApiClient |
110 | 149 | */ |
@@ -964,19 +1003,70 @@ public <T> ApiResponse<T> execute(Call call, final Type returnType, boolean... i |
964 | 1003 | responseInterceptorContext.setCommon(isCommon.length > 0 && isCommon[0]); |
965 | 1004 | responseInterceptorContext.setReturnType(returnType); |
966 | 1005 | context.setResponseContext(responseInterceptorContext); |
967 | | - |
968 | 1006 | context.setApiClient(this); |
969 | 1007 |
|
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 | + } |
979 | 1054 | } |
| 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; |
980 | 1070 | } |
981 | 1071 |
|
982 | 1072 | /** |
@@ -1011,34 +1101,76 @@ public <T> void executeAsync(Call call, final Type returnType, final ApiCallback |
1011 | 1101 | context.setResponseContext(responseInterceptorContext); |
1012 | 1102 |
|
1013 | 1103 | 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 | + ) { |
1014 | 1116 | try { |
1015 | 1117 | this.interceptorChain.executeRequest(context); |
1016 | 1118 | } catch (ApiException e) { |
1017 | 1119 | callback.onFailure(e, 0, null); |
1018 | 1120 | return; |
1019 | 1121 | } |
1020 | | - Callback okHttpCallBack = new Callback() { |
| 1122 | + |
| 1123 | + Call okhttpCall = getHttpClient().newCall(context.getRequestContext().getRequest()); |
| 1124 | + okhttpCall.enqueue(new Callback() { |
1021 | 1125 | @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 | + |
1024 | 1141 | } |
1025 | 1142 |
|
1026 | 1143 | @Override |
1027 | 1144 | 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 | + |
1029 | 1150 | try { |
1030 | 1151 | context.getResponseContext().setOriginalResponse(response); |
1031 | 1152 | context.getApiClient().interceptorChain.executeResponse(context); |
1032 | 1153 | result = (T) context.responseContext.getData(); |
| 1154 | + apiResponse = new ApiResponse<T>(response.code(), response.headers().toMultimap(), result); |
1033 | 1155 | } catch (ApiException e) { |
1034 | | - callback.onFailure(e, response.code(), response.headers().toMultimap()); |
1035 | | - return; |
| 1156 | + apiException = handleApiResponseException(e); |
1036 | 1157 | } |
1037 | | - callback.onSuccess(result, response.code(), response.headers().toMultimap()); |
1038 | | - } |
1039 | | - }; |
1040 | 1158 |
|
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 | + }); |
1042 | 1174 | } |
1043 | 1175 |
|
1044 | 1176 | /** |
@@ -1710,4 +1842,83 @@ public ApiClient setKeepAliveDurationMs(Integer keepAliveDurationMs) { |
1710 | 1842 | this.httpClient.setConnectionPool(new ConnectionPool(maxIdleConns, keepAliveDurationMs)); |
1711 | 1843 | return this; |
1712 | 1844 | } |
| 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 | + } |
1713 | 1924 | } |
0 commit comments