Skip to content

[FEATURE] etcdv3 registry: implement DoUnregister, DoUnsubscribe and reconnect subscription recovery #3366

Description

@XnLemon

⚠️ Verification

  • I have searched the issues of this repository and believe that this is not a duplicate.
  • I have searched the release notes of this repository and believe that this is not a duplicate.

🎯 Solution Description

The etcdv3 registry had three critical gaps compared to the zookeeper registry:

  1. DoUnregister was not implemented — always returned "DoUnregister is not support in etcdV3Registry".
    The fix deletes the corresponding etcd key via client.Delete, guarded by a client.Valid() check.

  2. DoUnsubscribe was not implemented — always returned an error.
    The fix introduces a per-service listener architecture: dataListener now holds a
    subscribed map[string]ConfigurationListener keyed by service key, replacing the old
    single shared configurationListener. Each DoSubscribe call creates an independent
    configurationListener for that service; DoUnsubscribe closes and removes it.

  3. Subscriptions were silently lost after reconnectInitListeners (called by RestartCallBack
    on reconnect) replaced dataListener with an empty new instance, discarding all active
    subscriptions. The fix stores subscribeURL *common.URL on each configurationListener and
    adds a recovery loop in InitListeners that mirrors the zookeeper registry: close stale
    listeners, create fresh ones, restart ListenServiceEvent goroutines for every recovered URL.

etcdv3 注册中心相比 zookeeper 注册中心存在三个关键缺口:

  1. DoUnregister 未实现 — 始终返回 "DoUnregister is not support in etcdV3Registry"
    修复:通过 client.Delete 删除对应 etcd key,调用前做 client.Valid() 校验。

  2. DoUnsubscribe 未实现 — 始终返回错误。
    修复:引入 per-service listener 架构,dataListener 改为以 service key 为键的
    subscribed map,取代原来全局共享的单个 configurationListener。每次 DoSubscribe
    为该服务创建独立的 configurationListenerDoUnsubscribe 关闭并移除对应 listener。

  3. 断线重连后订阅静默丢失InitListeners(重连时由 RestartCallBack 触发)将
    dataListener 替换为空实例,所有活跃订阅被丢弃。
    修复:在 configurationListener 上存储 subscribeURL *common.URL,在 InitListeners
    中加入与 zookeeper 注册中心对齐的恢复循环:关闭旧 listener,为每个已订阅 URL 创建新
    listener,重新启动 ListenServiceEvent goroutine。

📋 Use Cases

  • Services that need to deregister from etcd on graceful shutdown.

  • Services that dynamically subscribe/unsubscribe to providers at runtime.

  • Long-running applications deployed in environments with unstable etcd connections (container
    restarts, network partitions, etcd leader elections) that require seamless reconnect without
    losing active subscriptions.

  • 需要在优雅停机时从 etcd 注销的服务。

  • 运行时动态订阅/取消订阅 provider 的服务。

  • 部署在网络不稳定环境(容器重启、网络分区、etcd leader 选举)中、要求无感知重连且不丢失
    订阅的长期运行应用。

⚖️ Complexity & Risks

  • Medium complexity. The per-service listener refactor touches the core subscribe/unsubscribe path.

  • DataChange now iterates all subscribed keys (instead of returning on first match), which is
    a correctness improvement for wildcard/any-condition scenarios but changes the semantics slightly.

  • InitListeners holds the old dataListener mutex for the duration of recovery via defer,
    which may briefly delay concurrent DataChange calls — same trade-off as zookeeper.

  • 中等复杂度,per-service listener 重构影响核心订阅/取消订阅路径。

  • DataChange 改为遍历全部匹配 key(原先匹配即返回),对 wildcard/any-condition 场景是正确性
    修复,但语义有轻微变化。

  • InitListeners 通过 defer 持有旧 dataListener mutex 直至恢复完成,大量订阅时可能短暂
    阻塞并发 DataChange,与 zookeeper 注册中心权衡一致。

🔗 External Dependencies

None. Changes are confined to registry/etcdv3/listener.go and registry/etcdv3/registry.go,
using existing remoting/etcdv3 and common.URL APIs.

无外部依赖,改动限于 registry/etcdv3/listener.goregistry/etcdv3/registry.go
使用已有的 remoting/etcdv3common.URL API。

📘 Additional Context

Reference implementation for reconnect recovery: registry/zookeeper/registry.goInitListeners (line 92-120).

重连恢复逻辑参考实现:registry/zookeeper/registry.goInitListeners(第 92-120 行)。

如果觉得可以的话Assign me 然后我了解到我们目前需要将新注册中心迁移到dubbo-go-extension内作为插件使用 可以实现后进行迁移

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions