[apache/dubbo]新增动态配置无效

2024-07-02 384 views
3
  • Dubbo version: 3.0 branch
  • Operating System version: all
  • Java version: all
8833 改动忽略了新增配置的类型,导致使用 zkClient.create() 和 nacos 新增配置无效。
        if (event.getChangeType().equals(ConfigChangeType.ADDED)) {
            return;
        }

Nacos 获取配置类型

        private ConfigChangeType getChangeType(String configInfo, String oldValue) {
            if (StringUtils.isBlank(configInfo)) {
                return ConfigChangeType.DELETED;
            }
            if (StringUtils.isBlank(oldValue)) {
                return ConfigChangeType.ADDED;
            }
            return ConfigChangeType.MODIFIED;
        }

回答

3

zk的create事件通知内容只是个ip地址,如果对应ConfigChangeType.MODIFIED,那么genConfiguratorsFromRawRule解析内容时就会异常。 看NacosDynamicConfiguration、NacosMetadataReport代码,貌似nacos没有zk这样单独的create事件通知,可否把这两个类里的getChangeType改一下:

        private ConfigChangeType getChangeType(String configInfo, String oldValue) {
            if (StringUtils.isBlank(configInfo)) {
                return ConfigChangeType.DELETED;
            }
            /* nacos没有单独的create事件通知
            if (StringUtils.isBlank(oldValue)) {
                return ConfigChangeType.ADDED;
            }
            */
            return ConfigChangeType.MODIFIED;
        }
1

@haoyann 有相关的测试案例么?

4

@haoyann 有相关的测试案例么?

使用 dubbo-admin [服务治理/动态配置] 新增一个动态配置规则。 image 当前 dubbo-admin 使用 DynamicConfiguration 新增配置 https://github.com/apache/dubbo-admin/blob/906e87122f39527c54086db0d66fb04bcf746ba6/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/config/impl/MultiDynamicConfiguration.java#L80-L82 dynamicConfiguration.publishConfig(key, group, value) 对应 zookeeper 的实现就是使用的 zkClient.create

而且你提到的

zk的create事件通知内容只是个ip地址

这里应该排查一下为什么只是一个ip地址,因为这是服务治理所监听的路径,不会无故写入内容。而且在这个场景下处理 ADDED 是符合预期的。

9

之前zk的CacheListener的dataChanged只设置ConfigChangeType.DELETED和ConfigChangeType.MODIFIED,ADDED类型是是 #8821 加的。 这么做是当时发现dubbo构建有时会抛异常,比如: https://github.com/apache/dubbo/runs/3616815684?check_suite_focus=true 跟踪发现zk create常常先发一个data version版本为0的通知,事件的内容只是client ip地址,这种情况作为ConfigChangeType.MODIFIED被AbstractConfiguratorListener解析时就会异常。

5

改回原来的处理方式了,解析事件内容失败时的日志级别从error改为了warn,同时删掉了调用栈信息。

7

改回原来的处理方式了,解析事件内容失败时的日志级别从error改为了warn,同时删掉了调用栈信息。

多谢,不过如果原始的异常是在 CI 里面发现的,可以排查一下 CI 程序,看下为什么会写入一个 IP 地址进去。

5

dubbo-samples的dubbo-samples-applevel-override就能稳定重现:

  1. CuratorZookeeperClient的344行设置断点
            } else if (childData.getStat().getVersion() == 0) {
                content = new String(childData.getData(), CHARSET);
                eventType = EventType.NodeCreated;  <== line 344 set breakpoint here
            } else {
  2. 启动zk服务,端口2181
  3. debug启动BasicProvider, 启动参数加dubbo.port=20880
  4. debug启动BasicProvider, 启动参数加dubbo.port=20881
  5. 在ZKTools的66行设置断点:
            if (client.checkExists().forPath(path) == null) {
                client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path);  <== line 66 set breakpoint
            }
  6. debug执行DemoServiceIT单元测试
  7. 一直执行到DemoServiceIT到达ZKTools的66行断点处(出现CuratorZookeeperClient的344行断点时继续执行即可),之后单步执行完66行,两个provider都会再次执行到CuratorZookeeperClient的344行的断点处,此时查看两个provider的事件content内容就会看到里面都只是一个ip地址。

重复上述测试要先重新debug启动两个provider,再debug启动DemoServiceIT,否则到不了ZKTools的66行断点。

4

dubbo-samples-applevel-override集成测试代码本身应该没有问题,出问题是zk会单独抛节点创建事件(单步执行就会抛),但是多数情况下创建和后面的set设置貌似合并成了一个事件,所以dubbo-samples-applevel-override多数情况下并不会抛异常。 我没有测nacos,不清楚nacos是否存在zk类似的情况,除了nacos,还有很多种类的注册中心,不见得都是相同的规则。 为了兼容不同的注册中心,无论ADDED还是MODIFIED事件,还是都解析一下看看吧,解析异常时记录一行告警日志算了。

4

dubbo-samples-applevel-override的DemoServiceIT有通过UpgradeUtil.writeXxxRule()调用ZookeeperDynamicConfiguration.publishConfig(key, group, value)方法(最终调用的是doPublishConfig方法),跟了一下这个方法触发的事件只有consumer端的MigrationRuleListener在监听,并不涉及AbstractConfiguratorListener。 另外跟踪确认这个工程只有provider端在用AbstractConfiguratorListener监听zk事件,客户端调用zk.create并不会触发服务端的zk事件,客户端继续调用org.apache.curator.framework.imps.CreateBuilderImpl.forPath方法时才会触发服务端zk事件,其中触发服务端ADDED事件的时候,CreateBuilderImpl.forPath传给服务端的data是CuratorFrameworkFactory的defaultData,值是localAddress(本机地址),所以provider通过AbstractConfiguratorListener收到的事件content只是客户端的ip地址。 看起来dubbo consumer和provider的zk事件处理类是不一样的,所以AbstractConfiguratorListener应该不会导致出现zk新增动态配置无效的情况。

7

目前只有org.apache.dubbo.registry.integration.RegistryProtocol、 org.apache.dubbo.registry.integration.RegistryDirectory、org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory三个类的内部listener类是AbstractConfiguratorListener的派生类,通过AbstractConfiguratorListener的process方法解析事件内容,获得URL列表信息。

9

既然AbstractConfiguratorListener的process的目的就是解析URL,事件content如果是单个IP,也应视为URL(虽然不带参数的纯IP也没有什么用,后面的Configurator.toConfigurators方法还是会清空configurators),我重新改了一下PR,解析yaml异常就再用org.apache.dubbo.common.URL.valueOf处理一下,只要不是null就不告警了。

0

在 dubbo-admin 的界面上创建一个动态配置 nacos 接收到变更流程如下

image

image

8

下午没能重现issue现象的原因很弱鸡,断点设置的dubbo版本和dubbo-samples使用的dubbo版本不一致,所以设置的断点都没有执行到,改成一致版本之后,就可以跟踪了。 nacos和zk的确不一样,以dubbo-samples的dubbo-samples-nacos-override为例,启动BasicProvider时,同时有两个线程要解析服务实例的URL列表:

  1. 线程A执行RegistryProtocol的export方法:调用overrideUrlWithConfig创建ServiceConfigurationListener,通过AbstractConfiguratorListener的initWith方法,直接调用AbstractConfiguratorListener的genConfiguratorsFromRawRule解析URL列表;
  2. 线程B执行NacosDynamicConfiguration内部NacosConfigListener对象的innerReceive方法:通过AbstractConfiguratorListener的process方法(ADDED事件),调用AbstractConfiguratorListener的genConfiguratorsFromRawRule解析URL列表。

两个线程解析的rawConfig内容相同,解析出来的URL列表存入的configurators也是同一个对象,不清楚为什么由两个不同的线程重复解析。

Dubbo-admin我下载了develop当前分支,构建之后测了一下dubbo-samples-nacos-override的动态配置增、改、删,确认和述线程B执行过程相同(事件分别为ADDED、MODIFIED、DELETED)。

目前看nacos没有zk那样的单独创建node的事件。 @chickenlj @haoyann @CrazyHZM

更新: zk创建节点时如果用的是forPath(path, rawConfig.getBytes())而不是forPath(path),zk的ADDED事件content也是有内容的。 所以ADDED事件还是需要解析。