dubbo服务丢失问题

环境:dubbo-2.6.5(公司内部版本),注册中心:zookeeper

问题描述

生产服务有两个提供者,突然发现目前仅剩下了一个节点,登录丢失的节点,查看服务是正常的,而且没有任何error日志。

问题分析

推断1:dubbo admin页面展示问题,服务实际是正常提供的

查看服务的应用日志,发现服务没有任何实际的调用,也就是说,消费者已经真的看不到该提供者,故不成立。

查看日志

查看日志定位到服务丢失的时间点。发现当天出现zk出现了大量的超时,原因是当天的zk主节点宕机了。

推断2:dubbo zk上提供者节点数据丢失

查看zk下的提供者节点确实仅剩下了一个提供者节点数据。问题已定位那么原因是什么呢?

问题原因

问题是否出现在了dubbo对zk重连恢复数据这块,开始查源码。注册中心源码ZookeeperRegistry。

  1. 连接注册zk:通过zkclient添加zk状态监听。并且继承了FailbackRegistry各种失败重试。
public class ZookeeperRegistry extends FailbackRegistry {
    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        ...
        // 1. 连接zk
        zkClient = zookeeperTransporter.connect(url);
        // 2. 添加zk状态监听
        zkClient.addStateListener(new StateListener() {
            @Override
            public void stateChanged(int state) {
                // 3. 重新连接后恢复动作,将当前的注册服务于订阅任务添加至重试列表中等待重试
                if (state == RECONNECTED) {
                    try {
                        recover();
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });
    }
}

  1. zk客户端:默认使用CuratorZookeeperClient实现
public class CuratorZookeeperClient extends AbstractZookeeperClient<CuratorWatcher> {
    public CuratorZookeeperClient(URL url) {
        ...
            client = builder.build();
            // dubbo对接zk连接状态监听器
            client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
                @Override
                public void stateChanged(CuratorFramework client, ConnectionState state) {
                    if (state == ConnectionState.LOST) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.DISCONNECTED);
                    } else if (state == ConnectionState.CONNECTED) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.CONNECTED);
                    } else if (state == ConnectionState.RECONNECTED) {
                        CuratorZookeeperClient.this.stateChanged(StateListener.RECONNECTED);
                    }
                }
            });
            client.start();
        ...
    }
}
  1. 重试任务:注册重新失败重连任务FailbackRegistry中的DubboRegistryFailedRetryTimer,默认5秒检查一次是否需要失败恢复
public FailbackRegistry(URL url) {
    super(url);
    this.retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
    this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {
            // Check and connect to the registry
            try {
				// failedRegistered失败注册重试,failedUnregistered失败注销重试,failedSubscribed失败订阅重试,failedUnsubscribed失败取消订阅重试,failedNotified失败通知重试
                retry();
            } catch (Throwable t) { // Defensive fault tolerance
                logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
            }
        }
    }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}

通过三部分的代码我们可以推断,如果zk状态监听与恢复部分出现问题可能会导致数据丢失问题。于是查看相关的api并且尝试查看dubbo社区的问题与bug,果然发现了类似问题的修改与原因分析:https://github.com/apache/dubbo/pull/5135
问题的原因已经在代码的注释中说明,大体含义:如果ZNode数据已经存在,在会话超时期间,此时我们将重建一个数据节点,这个重复的异常原因可能是由于zk server中老的超时会话依然持有节点导致该节点的delete删除事件延迟,并且zk server还没有来得及去执行删除,可能由这种场景引起。在这个情景下,我们可以本地删除节点后再创建恢复节点数据。
个人理解是:如果会话断开连接又重新连接成功。断开连接发出的删除节点事件,因为延迟原因走在了重新连接恢复节点事件的后面。导致重新连接后没能成功恢复节点。也就是我么见到的,provider有一个节点服务正常,但是zk注册中心中的提供者节点数据丢失,导致出现该节点对其他订阅者不可见的现象

@Override
protected void createEphemeral(String path, String data) {
    byte[] dataBytes = data.getBytes(CHARSET);
    try {
        client.create().withMode(CreateMode.EPHEMERAL).forPath(path, dataBytes);
    } catch (NodeExistsException e) {
        logger.warn("ZNode " + path + " already exists, since we will only try to recreate a node on a session expiration" +
                ", this duplication might be caused by a delete delay from the zk server, which means the old expired session" +
                " may still holds this ZNode and the server just hasn't got time to do the deletion. In this case, " +
                "we can just try to delete and create again.", e);
        deletePath(path);
        createEphemeral(path, data);
    } catch (Exception e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值