首页 > 其他分享 >Dubbo-11191 用Maven插件应急修复Dubbo3.1.5以前的空指针异常

Dubbo-11191 用Maven插件应急修复Dubbo3.1.5以前的空指针异常

时间:2023-01-18 15:35:18浏览次数:51  
标签:11191 Dubbo 插件 dubbo applicationModel plugin jar maven

问题
java.lang.NullPointerException: null
    at org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation.getAllUnmodifiableSubscribedURLs(MetadataServiceDelegation.java:117)
    at org.apache.dubbo.registry.client.metadata.MetadataServiceDelegation.getSubscribedURLs(MetadataServiceDelegation.java:88)

推测Dubbo 3.1.2~3.1.4 之间均有这个问题。

原因

查看源码发现 getAllUnmodifiableSubscribedURLs 就是单纯没有进行空判断。

这个现象应该是Dubbo-Admin调用Dubbo的应用触发的这个bug。

现状

截至发文,2023年1月18日,Dubbo-3.1.5已经修复这个问题。

方法
  • 直接升级
    直接升级可能有未知风险,就像从2.x升级到3.1.2遇到这个问题一样。

  • 重新编译
    没必要重新编译发布到私服,这种办法一般是针对 BUG没有人发现、没人修复、迭代缓慢的项目。

  • 重写+构建
    通过SPI注册自己的实现类,修正他的行为,再结合maven打包插件来达成目的。
    这种方法不算简单,有点舍近求远,但是不依赖其他环境,不依赖自建maven仓库,契合原来的构建系统。

利用的是OOP编程的继承多态思想。

分析

Dubbo 使用了扩展点加载机制,也就是类SPI,本质上是一样的。

找到扩展点

/**
 * Listen for Dubbo application deployment events
 */
@SPI(scope = ExtensionScope.APPLICATION)
public interface ApplicationDeployListener extends DeployListener<ApplicationModel> {
    default void onModuleStarted(ApplicationModel applicationModel) {

    }
}

找到实现类

ExporterDeployListener
编写子类
点击查看代码
public class SlankkaHackedExporterDeployListener extends ExporterDeployListener {
    private static final Logger logger = LoggerFactory.getLogger(SlankkaHackedExporterDeployListener.class);

    //提前注入自己的MetadataServiceDelegation实例
    @Override
    public synchronized void onModuleStarted(ApplicationModel applicationModel) {
        logger.info("{} injected", SlankkaHackedExporterDeployListener.class.getName());
        applicationModel.getBeanFactory().registerBean(null, new SlankkaMetadataServiceDelegation(applicationModel));
        super.onModuleStarted(applicationModel);
    }

    static class URLComparator implements Comparator<URL> {
        //etc
    }
    //重写有问题的类,重写getSubscribedURLs方法(可以直接复制原来的)
    public static class SlankkaMetadataServiceDelegation extends MetadataServiceDelegation {

        public SlankkaMetadataServiceDelegation(ApplicationModel applicationModel) {
            super(applicationModel);
        }

        @Override
        public SortedSet<String> getSubscribedURLs() {
            SortedSet<URL> bizURLs = new TreeSet<>(URLComparator.INSTANCE);
            List<MetadataInfo> metadataInfos = getMetadataInfos();
            for (MetadataInfo metadataInfo: metadataInfos) {
                Map<String, SortedSet<URL>> serviceURLs = metadataInfo.getSubscribedServiceURLs();
                if (serviceURLs == null) {
                    continue;
                }
                for (Map.Entry<String, SortedSet<URL>> entry : serviceURLs.entrySet()) {
                    SortedSet<URL> urls = entry.getValue();
                    if (urls != null) {
                        for (URL url : urls) {
                            if (!MetadataService.class.getName().equals(url.getServiceInterface())) {
                                bizURLs.add(url);
                            }
                        }
                    }
                }
            }
            return MetadataService.toSortedStrings(bizURLs);
        }
    }
}

构建

新建一个模块(假设叫做 dubbo-hack,后面会提到),pom.xml中的关键信息:

点击查看代码
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.5.0</version>
    <executions>
        <execution>
            <id>unpack-dependencies</id>
            <phase>validate</phase>
            <goals>
                <goal>unpack</goal>
            </goals>
            <configuration>
                <artifactItems>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

其中 artifactItems 中应该包含两类信息:

  1. 要解压的jar:指定 dubbo 的GAV, 要排除 ExporterDeployListener 的SPI文件
  2. 要解压的jar的传递依赖: fastjson,fastjson2,com.alibaba.spring:spring-context-support等。

  1. dubbo
<artifactItem>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>${dubbo.version}</version>
    <type>jar</type>
    <overWrite>false</overWrite>
    <outputDirectory>${project.build.directory}/repackDubbo</outputDirectory>
    <destFileName>slankka-repacked.jar</destFileName>
    <includes>**/*</includes>
    <excludes>META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener</excludes>
</artifactItem>
  1. 这部分省略,和上面的类似。

到此 如果直接编译会将dubbo.jar自身的文件解压到outputDirectory文件夹内。

重新打包

这个时候可以用maven-assembly-plugin。

maven-assembly-plugin 的关键配置

<configuration>
    <!-- Name of new archive -->
    <finalName>dubbo-${dubbo.version}-slankka-solution</finalName>
    <!-- We don't want the id of the assembly descriptor in the filename -->
    <appendAssemblyId>false</appendAssemblyId>
    <descriptors>
        <!-- Descriptor of the contents of the new archive -->
        <descriptor>src/assembly/repack.xml</descriptor>
    </descriptors>
</configuration>

repack.xml的关键配置 fileSets

<fileSets>
    <fileSet>
        <!-- Files that are extracted from the original archive -->
        <directory>target/repackDubbo</directory>
        <outputDirectory/>
    </fileSet>
</fileSets>

到此 执行打包 会将解压后的文件重新生成一个jar。

最后一步

根据应用的不同,使用的最终打包工具不一样。

  • maven-shade-plugin
  • maven-jar-plugin
  • maven-war-plugin

这里项目用到的是war,但一行pom都不用改,只要war所在的pom,依赖了dubbo-hack这个模块,打包的程序会自动将生成的target下的jar 打进 war内。

SPI

由于实现类是放在 war所在的模块,写一份SPI文件来替换Dubbo自己的:
文件所在位置 META-INF/dubbo/internal/org.apache.dubbo.common.deploy.ApplicationDeployListener

exporter=com.slankka.dubbo.hack.SlankkaHackedExporterDeployListener

这里写的是子类

进一步优化

由于新建的专门打包dubbo用的模块 dubbo-hack,里面没有任何代码,不需要生成jar,因此可以关闭编译过程。
编译默认使用的maven-jar-plugin
因此可以通过配置maven-jar-plugin,来关闭这个过程。

声明一个 maven-jar-plugin,填写配置

<configuration>
    <skipIfEmpty>true</skipIfEmpty>
</configuration>

这样dubbo-hack生成的 target内只有一个dubbo的jar。
神奇的是,会被 war打包的过程中当成 dubbo-hack.jar,无论叫什么名字。

验证

服务启动以后,会出现一行日志

SlankkaHackedExporterDeployListener injected

标签:11191,Dubbo,插件,dubbo,applicationModel,plugin,jar,maven
From: https://www.cnblogs.com/slankka/p/17059525.html

相关文章

  • Android原生插件打包成AAR依赖丢失
    applyplugin:'com.android.library'applyplugin:'com.kezong.fat-aar'android{compileSdk30defaultConfig{minSdkVersion16targe......
  • jQuery插件(jQuery-UI(Accordion:手风琴/Autocomplete:自动搜索匹配/Tabs:选项卡))
    视频<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>测试jQuery-UI的基本使用</title><linkrel="stylesheet"href="jquery-ui.css"><......
  • jQuery插件(jQuery validation)
    视频<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><style>.error{color:#F00;}</style></hea......
  • IntelliJ插件笔记(二) 运行插件---添加Actions
    本文内容主要流程按照官网手册展开,也是较为准确的翻译。原文可见:CreatingActions|IntelliJPlatformPluginSDK(jetbrains.com)Actions插件可以在IDE的菜单或者工具......
  • maven flatten-maven-plugin 插件简单说明
    mavenflatten-maven-plugin是一个比较有意思的项目,核心是对于我们发布的包提供一些简洁清晰的使用,去除掉哪些没用的(比如父pom信息,profile信息。。。)参考使用maven......
  • React:开发者工具谷歌插件下载安装
    React:开发者工具谷歌插件最近学习前端react主要是想大概浏览一下,这里提供尚硅谷的是视屏资料中的谷歌插件的下载。(如有侵权联系删除)这里提供我的下载地址,其中除了插件,还有......
  • dubbo
    参考说明https://promotion.aliyun.com/ntms/act/edasdubbo.html?utm_content=se_1005011324#阿里云微服务https://help.aliyun.com/document_detail/992......
  • Kong网关安装自定义插件
    安装自定义插件需要注意kong网关的版本要求!! 下面以安装Skywalking插件为例,要求Kong网关是2.2及以上版本,https://github.com/apache/skywalking-kong一、下载Skywalking......
  • 生成二维码插件&用法
        1)vue-qrcode:     git地址:https://github.com/fengyuanchen/vue-qrcode     用法:        安装依赖:npminstallqrcode......
  • 前端打印插件jqprint的使用
    1、下载地址https://webscripts.softpedia.com/script/Modules/jQuery-Plugins/jqPrint-68448.html2、使用jqprint必须在使用引用插件之前加上jquery-migrate-1.2.1.min.j......