dianping / cat

CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
Apache License 2.0
18.63k stars 5.42k forks source link

[Integration] Cat integration CatRegistryFactoryWrapper NullPointerException #2343

Open slankka opened 1 month ago

slankka commented 1 month ago

Describe the bug CatRegistryFactoryWrapper has a potential NPE bug

To Reproduce

  1. install artifact cat-monitor Dubbo整合插件
  2. start application
<dependency>
       <groupId>net.dubboclub</groupId>
        <artifactId>cat-monitor</artifactId>
         <version>x.x.x</version>
</dependency>

Expected behavior normal startup

Versions Dubbo 3.1.6 Spring 5.2.8.RELEASE

Exception

02 八月 2024 16:51:53,669 175090 [localhost-startStop-1] INFO  org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils  -  [DUBBO] Start registering instance address to registry., dubbo version: 3.1.6, current host: 172.17.0.1
02 八月 2024 16:51:53,696 175117 [localhost-startStop-1] WARN  org.apache.dubbo.registry.client.AbstractServiceDiscovery  -  [DUBBO] No valid instance found, stop registering instance address to registry., dubbo version: 3.1.6, current host: 172.17.0.1, error code: 
1-12. This may be caused by , go to https://dubbo.apache.org/faq/1/12 to find instructions.
02 八月 2024 16:51:53,698 175119 [localhost-startStop-1] ERROR org.apache.dubbo.config.deploy.DefaultApplicationDeployer  -  [DUBBO] Dubbo Application[1.1](xxxx-plat-rdc) found failed module: Dubbo Module[1.1.1], dubbo version: 3.1.6, current host: 172.17.0.1, error code: 5-14. This may be caused by , go to https://dubbo.apache.org/faq/5/14 to find instructions.
java.lang.NullPointerException
        at net.dubboclub.catmonitor.registry.registry.CatRegistryFactoryWrapper$RegistryWrapper.register(CatRegistryFactoryWrapper.java:58)
        at org.apache.dubbo.registry.ListenerRegistryWrapper.register(ListenerRegistryWrapper.java:62)
        at org.apache.dubbo.registry.integration.RegistryProtocol.register(RegistryProtocol.java:212)
        at org.apache.dubbo.registry.integration.RegistryProtocol.export(RegistryProtocol.java:250)
        at org.apache.dubbo.qos.protocol.QosProtocolWrapper.export(QosProtocolWrapper.java:77)
        at org.apache.dubbo.rpc.protocol.ProtocolSecurityWrapper.export(ProtocolSecurityWrapper.java:80)
        at org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper.export(ProtocolListenerWrapper.java:64)
        at org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper.export(ProtocolFilterWrapper.java:58)
        at org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper.export(ProtocolSerializationWrapper.java:47)
        at org.apache.dubbo.rpc.Protocol$Adaptive.export(Protocol$Adaptive.java)
        at org.apache.dubbo.config.ServiceConfig.doExportUrl(ServiceConfig.java:717)
        at org.apache.dubbo.config.ServiceConfig.exportRemote(ServiceConfig.java:695)
        at org.apache.dubbo.config.ServiceConfig.exportUrl(ServiceConfig.java:636)
        at org.apache.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol(ServiceConfig.java:445)
        at org.apache.dubbo.config.ServiceConfig.doExportUrls(ServiceConfig.java:429)
        at org.apache.dubbo.config.ServiceConfig.doExport(ServiceConfig.java:391)
        at org.apache.dubbo.config.ServiceConfig.export(ServiceConfig.java:243)
        at org.apache.dubbo.config.deploy.DefaultModuleDeployer.exportServiceInternal(DefaultModuleDeployer.java:350)
        at org.apache.dubbo.config.deploy.DefaultModuleDeployer.exportServices(DefaultModuleDeployer.java:322)
        at org.apache.dubbo.config.deploy.DefaultModuleDeployer.startSync(DefaultModuleDeployer.java:158)
        at org.apache.dubbo.config.deploy.DefaultModuleDeployer.start(DefaultModuleDeployer.java:139)
        at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onContextRefreshedEvent(DubboDeployApplicationListener.java:113)
        at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onApplicationEvent(DubboDeployApplicationListener.java:102)
        at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onApplicationEvent(DubboDeployApplicationListener.java:47)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
slankka commented 1 month ago

https://github.com/dianping/cat/blob/60693f75f6ccc34ac9dc73b5c44526a7d2f20b35/integration/apache-dubbo/src/main/java/net/dubboclub/catmonitor/registry/CatRegistryFactoryWrapper.java#L37

经过Debug 构造函数这里接收了空值,难以复现,但2024年了,还是遇到了

    @Override
    public Registry getRegistry(URL url) {
        return new RegistryWrapper(registryFactory.getRegistry(url)); // registryFactory.getRegistry = null
    }

    class RegistryWrapper implements Registry {
        private Registry originRegistry;

        public RegistryWrapper(Registry originRegistry) {
            this.originRegistry = originRegistry;
        }
......
        @Override
        public void register(URL url) {
            originRegistry.register(appendProviderAppName(url)); //NPE : originRegistry = null
        }
slankka commented 1 month ago

临时修复建议

    @Override
    public Registry getRegistry(URL url) {
        Registry registry = registryFactory.getRegistry(url);
        if (registry == null) {
            logger.info("registry is null url " + url);
            return registry;
        }
        return new RegistryWrapper(registry);
    }

依据:可以看到 Dubbo 的 ListenerRegistryWrapper 进行了registry != null 判断,并且从该PR开始,getRegistry根据check进行判断,check=false的时候,registry是可以等于null的

https://github.com/apache/dubbo/pull/8483

public class ListenerRegistryWrapper implements Registry {

   @Override
    public void register(URL url) {
        try {
            if (registry != null) {
                registry.register(url);
            }
        } finally {
            if (!UrlUtils.isConsumer(url)) {
                listenerEvent(serviceListener -> serviceListener.onRegister(url, registry));
            }
        }
    }
}

依据2:

public abstract class AbstractRegistryFactory implements RegistryFactory { 
        ......

       if (check && registry == null) {
            throw new IllegalStateException("Can not create registry " + url);
        }

        if (registry != null) {
            REGISTRIES.put(key, registry);
        }
        return registry;
    }
}