Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 64 additions & 84 deletions src/main/java/com/tencentcloudapi/common/AbstractClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ public abstract class AbstractClient {
// Handles HTTP connections.
private HttpConnection httpConnection;

// Circuit breaker for handling region failures.
private CircuitBreaker regionBreaker;

/**
* Constructor for AbstractClient with default client profile.
*
Expand Down Expand Up @@ -134,9 +131,11 @@ public AbstractClient(
this.profile.getHttpProfile().getWriteTimeout()
);
this.httpConnection.addInterceptors(this.log);
if (this.profile.getHttpProfile().getDomainFailover()) {
this.httpConnection.addInterceptors(new EndpointFailoverInterceptor(this));
}
this.trySetProxy(this.httpConnection);
this.trySetSSLSocketFactory(this.httpConnection);
this.trySetRegionBreaker();
this.trySetHostnameVerifier(this.httpConnection);
this.trySetHttpClient();
warmup();
Expand Down Expand Up @@ -435,13 +434,6 @@ private void trySetHostnameVerifier(HttpConnection conn) {
}
}

private void trySetRegionBreaker() {
String ep = profile.getBackupEndpoint();
if (ep != null && !ep.isEmpty()) {
this.regionBreaker = new CircuitBreaker();
}
}

private void trySetHttpClient() {
Object httpClient = profile.getHttpProfile().getHttpClient();
if (httpClient != null) {
Expand All @@ -453,6 +445,9 @@ private void trySetHttpClient() {
* Executes an API request and returns the raw string response.
* Handles circuit breaking for region failover.
*
/**
* Executes an API request and returns the raw string response.
*
* @param request The request object containing API parameters.
* @param actionName The name of the API action to be called.
* @return The raw string response from the API.
Expand All @@ -461,31 +456,15 @@ private void trySetHttpClient() {
protected String internalRequest(AbstractModel request, String actionName)
throws TencentCloudSDKException {

CircuitBreaker.Token breakerToken = null;
// Attempt to acquire a token from the circuit breaker.
// If the circuit is open, use the backup endpoint.
if (regionBreaker != null) {
breakerToken = regionBreaker.allow();
if (!breakerToken.allowed) {
endpoint = service + "." + profile.getBackupEndpoint();
}
}

Response okRsp;
try {
// Execute the raw API request.
okRsp = internalRequestRaw(request, actionName);
} catch (IOException e) {
// Network failure: report to circuit breaker and throw exception.
if (breakerToken != null) {
breakerToken.report(false);
}
throw new TencentCloudSDKException("", e);
}

String strResp;
try {
// Extract the response body as a string.
strResp = okRsp.body().string();
} catch (IOException e) {
String msg = "Cannot transfer response body to string, because Content-Length is too large, or " +
Expand All @@ -496,29 +475,16 @@ protected String internalRequest(AbstractModel request, String actionName)

JsonResponseModel<JsonResponseErrModel> errResp;
try {
// Deserialize the response to check for errors.
Type errType = new TypeToken<JsonResponseModel<JsonResponseErrModel>>() {
}.getType();
errResp = gson.fromJson(strResp, errType);
} catch (JsonSyntaxException e) {
// Invalid JSON response: log and throw exception.
String msg = "json is not a valid representation for an object of type";
log.info(msg);
throw new TencentCloudSDKException(msg, e);
}

// Check for API errors in the response.
if (errResp.response.error != null) {
if (breakerToken != null) {
// Report the success/failure of the request to the circuit breaker.
JsonResponseErrModel error = errResp.response;
// Consider a region "OK" if we get a valid requestId and no InternalError.
boolean regionOk = error.requestId != null
&& !error.requestId.isEmpty()
&& error.error.code != null
&& !error.error.code.equals("InternalError");
breakerToken.report(regionOk);
}
throw new TencentCloudSDKException(
errResp.response.error.message,
errResp.response.requestId,
Expand All @@ -530,7 +496,6 @@ protected String internalRequest(AbstractModel request, String actionName)

/**
* Executes an API request and returns the deserialized response object.
* Handles circuit breaking for region failover.
*
* @param request The request object containing API parameters.
* @param actionName The name of the API action to be called.
Expand All @@ -541,67 +506,62 @@ protected String internalRequest(AbstractModel request, String actionName)
*/
protected <T> T internalRequest(AbstractModel request, String actionName, Class<T> typeOfT)
throws TencentCloudSDKException {
CircuitBreaker.Token breakerToken = null;
// Attempt to acquire a token from the circuit breaker.
// If the circuit is open, use the backup endpoint.
if (regionBreaker != null) {
breakerToken = regionBreaker.allow();
if (!breakerToken.allowed) {
endpoint = service + "." + profile.getBackupEndpoint();
}
}

try {
Response resp = internalRequestRaw(request, actionName);
if (Objects.equals(resp.header("Content-Type"), "text/event-stream")) {
return processResponseSSE(resp, typeOfT, breakerToken);
return processResponseSSE(resp, typeOfT);
}
return processResponseJson(resp, typeOfT, breakerToken);
return processResponseJson(resp, typeOfT);
} catch (IOException e) {
// Network failure: report to circuit breaker and throw exception.
if (breakerToken != null) {
breakerToken.report(false);
}
throw new TencentCloudSDKException("", e);
}
}

/**
* Processes a Server-Sent Events (SSE) response.
*
* @param resp The raw HTTP response.
* @param typeOfT The class of the response model.
* @param breakerToken The circuit breaker token.
* @param <T> The type of the response model.
* @param resp The raw HTTP response.
* @param typeOfT The class of the response model.
* @param <T> The type of the response model.
* @return The SSE response model.
* @throws TencentCloudSDKException If an error occurs during processing.
*/
protected <T> T processResponseSSE(Response resp, Class<T> typeOfT, CircuitBreaker.Token breakerToken) throws TencentCloudSDKException {
protected <T> T processResponseSSE(Response resp, Class<T> typeOfT) throws TencentCloudSDKException {
SSEResponseModel responseModel;
try {
// Create a new instance of the response model.
responseModel = (SSEResponseModel) typeOfT.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new TencentCloudSDKException("", e);
}
// Set request ID and circuit breaker token in the response model.
responseModel.setRequestId(resp.header("X-TC-RequestId"));
responseModel.setToken(breakerToken);
responseModel.setResponse(resp);
return (T) responseModel;
}

/**
* Legacy three-arg overload. The {@code breakerToken} is ignored — region
* failover is now handled by {@link EndpointFailoverInterceptor} at the HTTP
* layer, not via a per-call CircuitBreaker token. Kept so subclasses or
* external callers compiled against earlier SDK versions still link.
*
* @deprecated Use {@link #processResponseSSE(Response, Class)} instead.
*/
@Deprecated
protected <T> T processResponseSSE(Response resp, Class<T> typeOfT, CircuitBreaker.Token breakerToken)
throws TencentCloudSDKException {
return processResponseSSE(resp, typeOfT);
}

/**
* Processes a JSON response.
*
* @param resp The raw HTTP response.
* @param typeOfT The class of the response object to deserialize to.
* @param breakerToken The circuit breaker token.
* @param <T> The type of the response object.
* @param resp The raw HTTP response.
* @param typeOfT The class of the response object to deserialize to.
* @param <T> The type of the response object.
* @return The deserialized response object.
* @throws TencentCloudSDKException If an error occurs during processing.
*/
protected <T> T processResponseJson(Response resp, Class<T> typeOfT, CircuitBreaker.Token breakerToken) throws TencentCloudSDKException {
protected <T> T processResponseJson(Response resp, Class<T> typeOfT) throws TencentCloudSDKException {
String body;
try {
body = resp.body().string();
Expand All @@ -623,29 +583,31 @@ protected <T> T processResponseJson(Response resp, Class<T> typeOfT, CircuitBrea
throw new TencentCloudSDKException(msg, e);
}

// Check for API errors in the response.
if (errResp.response.error != null) {
if (breakerToken != null) {
// Report the success/failure of the request to the circuit breaker.
JsonResponseErrModel error = errResp.response;
// Consider a region "OK" if we get a valid requestId and no InternalError.
boolean regionOk = error.requestId != null
&& !error.requestId.isEmpty()
&& error.error.code != null
&& !error.error.code.equals("InternalError");
breakerToken.report(regionOk);
}
throw new TencentCloudSDKException(
errResp.response.error.message,
errResp.response.requestId,
errResp.response.error.code);
}

// Deserialize the successful response into the desired object type.
Type type = TypeToken.getParameterized(JsonResponseModel.class, typeOfT).getType();
return ((JsonResponseModel<T>) gson.fromJson(body, type)).response;
}

/**
* Legacy three-arg overload. The {@code breakerToken} is ignored — region
* failover is now handled by {@link EndpointFailoverInterceptor} at the HTTP
* layer, not via a per-call CircuitBreaker token. Kept so subclasses or
* external callers compiled against earlier SDK versions still link.
*
* @deprecated Use {@link #processResponseJson(Response, Class)} instead.
*/
@Deprecated
protected <T> T processResponseJson(Response resp, Class<T> typeOfT, CircuitBreaker.Token breakerToken)
throws TencentCloudSDKException {
return processResponseJson(resp, typeOfT);
}

/**
* Executes the raw API request and returns the HTTP Response object.
*
Expand Down Expand Up @@ -1087,11 +1049,29 @@ public Object retry(AbstractModel req, int retryTimes) throws TencentCloudSDKExc
return null;
}

/**
* Region-level failover is now handled by {@link EndpointFailoverInterceptor};
* this client no longer holds a region {@link CircuitBreaker}. Always returns
* {@code null}. Kept for source/binary compatibility with code that called
* the old getter.
*
* @deprecated Failover is wired up automatically; this accessor is obsolete.
*/
@Deprecated
public CircuitBreaker getRegionBreaker() {
return regionBreaker;
return null;
}

/**
* No-op. Region-level failover is now handled by
* {@link EndpointFailoverInterceptor}; assigning a {@link CircuitBreaker} here
* has no effect.
*
* @deprecated Failover is wired up automatically; this setter is obsolete.
*/
@Deprecated
public void setRegionBreaker(CircuitBreaker regionBreaker) {
this.regionBreaker = regionBreaker;
// intentionally empty
}

}
Loading