Dubbo重试机制

🚀 4. 整体重试控制策略

在主流程如 FailbackRegistry#register() 方法中可以看到完整的错误处理逻辑:

  • register 方法是 FailbackRegistry 类的核心方法之一,用于向注册中心注册服务提供者URL

  • 此方法重写了父类 AbstractRegistry 的注册方法,增加了失败自动重试机制

@Override
public void register(URL url) {
    if (!shouldRegister(url)) {
        return;
    }
   
    super.register(url);
    removeFailedRegistered(url);  // 清理之前可能存在的失败任务
    removeFailedUnregistered(url);
    try {
        // 向服务器端发送注册请求, 实际注册操作
        doRegister(url);
    } catch (Exception e) {
        Throwable t = e;
		// ... 错误判断和日志输出 ..
        // 将失败的注册请求记录到失败列表中,定期重试
        addFailedRegistered(url);
    }
}

只有在初次尝试失败且不需要立刻抛出异常的情况下才会进入重试流程。

✅ 1. 参数检查

if (!shouldRegister(url)) {
    return;
}
  • 首先检查是否应该注册该URL,如果不满足条件则直接返回。检查包括:
    • 是否为额外协议(extra protocol)
    • 当前注册中心是否接受该协议类型的服务

✅ 2. 调用父类注册逻辑

super.register(url);
  • 调用父类 AbstractRegistry 的注册方法,更新本地缓存状态

✅ 3. 清理失败任务

removeFailedRegistered(url);
removeFailedUnregistered(url);
  • 清除之前可能存在的注册和取消注册失败任务,因为现在正在进行新的注册操作。

✅ 4. 执行实际注册

try {
    // 向服务器端发送注册请求, 实际注册操作
    doRegister(url);
} catch (Exception e) {
  • 调用抽象方法 doRegister 执行真正的注册逻辑,这个方法由具体的注册中心实现类提供

✅ 5. 异常处理机制

  • 当 doRegister 操作失败时,会进行以下处理:
... catch (Exception e) {
    Throwable t = e;
    // ... 错误判断和日志输出 ..
    Throwable t = e;

    // If the startup detection is opened, the Exception is thrown directly.
    boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
        && url.getParameter(Constants.CHECK_KEY, true)
        && (url.getPort() != 0);
    boolean skipFailback = t instanceof SkipFailbackWrapperException;
    if (check || skipFailback) {
        if (skipFailback) {
            t = t.getCause();
        }
        throw new IllegalStateException(
            "Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: "
            + t.getMessage(),
            t);
    } else {
        logger.error(
            INTERNAL_ERROR,
            "unknown error in registry module",
            "",
            "Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(),
            t);
    }
    // 将失败的注册请求记录到失败列表中,定期重试
    addFailedRegistered(url);
}

简化一下之后:

boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
        && url.getParameter(Constants.CHECK_KEY, true)
        && (url.getPort() != 0);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
  • 决定是否跳过失败回退机制:
    • check: 启动检测是否开启
    • skipFailback: 是否为可跳过的异常类型

直接抛出异常的情况:

if (check || skipFailback) {
    if (skipFailback) {
        t = t.getCause();
    }
    throw new IllegalStateException(
            "Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: "
                    + t.getMessage(),
            t);
}
  • 当启用启动检测或者遇到特定异常时,直接抛出异常终止流程。

✅ 6. 记录日志安排重试

else {
    logger.error(
            INTERNAL_ERROR,
            "unknown error in registry module",
            "",
            "Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(),
            t);
}

// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
  • 否则记录错误日志并将失败任务加入重试队列。

4.2.4 设计思想

  • 🚀 容错设计理念

    • Dubbo 在设计上遵循了"尽力而为"的原则,即对于非关键路径上的操作(如服务注册),即使暂时失败也不会影响整个系统的运行,而是通过后台不断重试直到成功为止。
    • 提供了自动重试功能,防止因网络抖动等原因导致的注册失败
    • 支持配置是否启用严格检查模式
  • ✅ 分布式系统稳定性保障

    • 由于分布式环境中的网络波动等问题不可避免,这种自动重试机制能够显著提升系统对外部依赖故障的容忍能力,增强整体可用性和鲁棒性。
  • ⬆️ 资源高效利用

    • 采用共享的时间轮计时器统一管理所有重试任务,避免了大量的线程开销,同时保证了合理的调度频率。
  • 🔝 扩展性强的设计模式

    • 通过定义抽象方法(如 doRegister, doSubscribe 等),子类只需要关注具体协议层面的操作即可,父类统一负责通用的容错处理逻辑。
  • 🎉 幂等性保证

    • 在每次注册前都会清理之前的失败任务,避免重复注册
    • 通过 ConcurrentMap 和 putIfAbsent 确保同一URL不会重复添加到失败队列
  • 🆕 可扩展性

    • 将具体注册逻辑抽象成 doRegister 方法,由子类实现不同的注册中心协议

4.2.5 与其它组件的关系

  • 与 HashedWheelTimer 配合实现定时重试
  • 与 FailedRegisteredTask 协作管理失败的注册任务
  • 继承自 AbstractRegistry 获取基础的注册管理功能

4.2.6 addFailedRegistered

  • 重试任务
private void addFailedRegistered(URL url) {
    FailedRegisteredTask oldOne = failedRegistered.get(url);
    if (oldOne != null) {
        return;
    }
    FailedRegisteredTask newTask = new FailedRegisteredTask(url, this);
    oldOne = failedRegistered.putIfAbsent(url, newTask);
    if (oldOne == null) {
        // never has a retry task. then start a new task for retry.
        retryTimer.newTimeout(newTask, retryPeriod, TimeUnit.MILLISECONDS);
    }
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐