保障 REST 服务高可用

更新时间不可用

了解如何在智能云会议中保障 REST 服务高可用,包括适用场景、配置步骤、实现要点和注意事项。

为保障 REST 服务的高可用性,避免因区域网络抖动、故障、DNS 解析失败或服务异常导致的请求失败,声网提供了完整的容错方案,包括多域名切换和指数退避重试机制。

为什么需要容错机制?

在分布式系统中,网络请求可能因以下原因失败:

  • DNS 解析故障:域名解析到故障节点
  • 网络波动:区域网络不稳定导致超时
  • 服务异常:目标服务器临时不可用(50x 错误)
  • 跨域访问:地理位置导致的延迟或丢包

通过多域名切换和退避重试,可以将服务可用性从 99% 提升至 99.99% 以上。

容错策略

触发条件

在以下情况下触发重试机制:

  • 请求超时(建议超时时间:10-30秒)
  • HTTP 状态码 50x(500、502、503、504等服务端错误)
  • 域名解析失败(DNS 解析超时或无法解析)

重试策略

你可以采用以下重试策略:

阶段重试次数等待时间域名类型说明
初始请求--主域名根据服务器地理位置选择
第1次重试11秒主域名同域名立即重试
第2次重试23秒备用主域名切换到备用全局域名
第3次重试36秒区域域名1切换到相邻区域
第4次重试410秒区域域名2切换到另一相邻区域
表格内容超出当前宽度,可横向滚动查看更多列。

总计:最多 5 次请求(1 次初始 + 4 次重试),累计等待时间约 20 秒。

域名选择规则

根据你的业务服务器所在地选择主域名:

  • 中国大陆:优先使用 api.sd-rtn.com,备用 api.agora.io
  • 海外地区:优先使用 api.agora.io,备用 api.sd-rtn.com

信息

切换到区域域名前,请确保目标区域已部署你使用的服务(如云端录制、实时消息等)。

域名信息

下表列出了主域名及其对应的区域域名信息。

主域名区域域名地理区域
api.sd-rtn.comapi-us-west-1.sd-rtn.com美国西部
api-us-east-1.sd-rtn.com美国东部
api-ap-southeast-1.sd-rtn.com亚太东南
api-ap-northeast-1.sd-rtn.com亚太东北
api-eu-west-1.sd-rtn.com欧洲西部
api-eu-central-1.sd-rtn.com欧洲中部
api-cn-east-1.sd-rtn.com中国华东
api-cn-north-1.sd-rtn.com中国华北
api.agora.ioapi-us-west-1.agora.io美国西部
api-us-east-1.agora.io美国东部
api-ap-southeast-1.agora.io亚太东南
api-ap-northeast-1.agora.io亚太东北
api-eu-west-1.agora.io欧洲西部
api-eu-central-1.agora.io欧洲中部
api-cn-east-1.agora.io中国华东
api-cn-north-1.agora.io中国华北
表格内容超出当前宽度,可横向滚动查看更多列。

完整重试流程

流程图

伪代码实现

以下伪代码展示了 RESTful API 请求容错重试的实现逻辑。

Java
/**
 * RESTful API 请求容错重试
 */
public class RestfulApiClient {
    
    // 域名配置 - 中国区
    private static final String[] DOMAINS_CN = {
        "api.sd-rtn.com",                  // 主域名
        "api.agora.io",                    // 备用主域名
        "api-cn-east-1.sd-rtn.com",        // 区域域名 - 中国华东
        "api-cn-north-1.sd-rtn.com"        // 区域域名 - 中国华北
    };
    
    // 域名配置 - 海外
    private static final String[] DOMAINS_OVERSEAS = {
        "api.agora.io",                    // 主域名
        "api.sd-rtn.com",                  // 备用主域名
        "api-us-west-1.agora.io",          // 区域域名 - 美国西部
        "api-us-east-1.agora.io"           // 区域域名 - 美国东部
    };
    
    // 退避时间(毫秒)
    private static final int[] BACKOFF_DELAYS = {0, 1000, 3000, 6000, 10000};
    
    // 最大重试次数
    private static final int MAX_RETRIES = 4;
    
    // 请求超时时间(毫秒)
    private static final int TIMEOUT = 10000;
    
    /**
     * 发起带容错的 HTTP 请求
     * 
     * @param path    请求路径
     * @param method  HTTP 方法
     * @param body    请求 Body
     * @param isChina 是否中国区
     * @return 响应结果
     */
    public Response requestWithRetry(String path, String method, Object body, boolean isChina) {
        String[] domains = isChina ? DOMAINS_CN : DOMAINS_OVERSEAS;
        int retryCount = 0;
        Exception lastException = null;
        
        while (retryCount <= MAX_RETRIES) {
            // 1. 选择域名(根据重试次数切换)
            int domainIndex = Math.min(retryCount, domains.length - 1);
            String domain = domains[domainIndex];
            String url = "https://" + domain + path;
            
            // 2. 等待退避时间(首次请求不等待)
            if (retryCount > 0) {
                sleep(BACKOFF_DELAYS[retryCount]);
                log.info("第{}次重试,使用域名: {}", retryCount, domain);
            }
            
            try {
                // 3. 发起 HTTP 请求
                Response response = httpRequest(url, method, body, TIMEOUT);
                
                // 4. 请求成功,返回结果
                if (isSuccess(response)) {
                    return response;
                }
                
                // 5. 服务端错误(50x),触发重试
                if (isServerError(response)) {
                    log.warn("服务端错误: {} {}", response.getStatus(), url);
                    lastException = new ServerException(response.getStatus());
                } else {
                    // 客户端错误(4xx),不重试,直接返回
                    return response;
                }
                
            } catch (TimeoutException e) {
                log.warn("请求超时: {}", url);
                lastException = e;
            } catch (NetworkException e) {
                log.warn("网络异常: {} - {}", url, e.getMessage());
                lastException = e;
            }
            
            retryCount++;
        }
        
        // 所有重试均失败,抛出异常
        throw new ApiRequestException("请求失败,已重试" + MAX_RETRIES + "次", lastException);
    }
    
    /**
     * 判断是否成功响应(2xx)
     */
    private boolean isSuccess(Response response) {
        return response.getStatus() >= 200 && response.getStatus() < 300;
    }
    
    /**
     * 判断是否服务端错误(需要重试)
     */
    private boolean isServerError(Response response) {
        int status = response.getStatus();
        return status == 500 || status == 502 || status == 503 || status == 504;
    }
}

使用示例

下面的示例展示了中国区和海外请求的调用方式。

Java
RestfulApiClient client = new RestfulApiClient();

// 中国区请求
Response response = client.requestWithRetry(
    "/cn/conference/apps/{appId}/v1/rooms",
    "POST",
    requestBody,
    true  // isChina = true
);

// 海外请求
Response response = client.requestWithRetry(
    "/conference/apps/{appId}/v1/rooms",
    "POST",
    requestBody,
    false  // isChina = false
);

开发注意事项

  1. 幂等性:确保重试的接口是幂等的(GET、PUT、DELETE),对于非幂等的 POST 请求重复执行会产生多个新资源或重复操作,可考虑引入幂等键等机制
  2. 超时设置:建议设置 5-10 秒超时,过短可能导致误判
  3. 日志记录:记录每次重试的域名和错误信息,便于排查问题
  4. 监控告警:当重试次数频繁时,应触发告警检查服务状态