maven 如何忽略指定的远程仓库
问题背景
最近在进行云平台提供商迁移的过程中遇到一个跟 maven 仓库相关的有趣的问题,跟大家分享一下。
由于公司 nexus 私服迁移到新机器上,启用了新的地址,原来的地址下线,在 Jenkins 打包机器上重新配置好 settings.xml 文件后,发现还是有部分应用在拉取依赖包时仍然访问老的 nexus 私服地址,导致卡住进而超时,如下图所示:
经过排查,发现这是因为不少业务方在上传 jar 包到私服时指定了仓库地址(老的地址),而很多 jar 包配置都是互相 copy 的,导致这样的包含老地址的 jar 包非常多,如下所示:
maven 远程仓库的顺序
遇到这个问题的时候其实有点诧异,因为按照官方文档,应该是全局 settings.xml 或者 .m2/settings.xml 中配置的仓库地址优先,不应该出现类似问题才对。
effective settings:Global
settings.xml
Usersettings.xml
local effective build POM:Local
pom.xml
Parent POMs, recursivelySuper POMeffective POMs from dependency path to the artifact.
可真实情况是,在下载依赖包时,依赖包中定义的仓库地址也会生效,并且会和 settings.xml 中定义的仓库地址、当前构建项目中指定的仓库地址组成一个候选仓库地址列表,同时发起下载请求,并且会尝试更新一些 SNAPSHOT 包的 metadata 信息。
举个例子,假设当前构建项目为 A,A 中定义了仓库地址 repoA,依赖 b.jar,b.jar 的 pom.xml 中自定义了仓库地址 repoB,此时 settings.xml 定义仓库地址为 repo,那么在下载 b.jar 以及 b.jar 传递依赖的其他依赖包时,都会同时使用到 repoA, repoB 及 repo,如下图所示:
那么面对这种旧仓库地址已经下线,而很多 jar 包仍然依赖着老地址的尴尬局面,该如何解决呢?总不能让业务方都重新发布一遍 jar 包,替换旧仓库地址吧,这样做法工作量巨大,而且很多 jar 包都没人维护了,所以必须寻找其他解决方案。
解决方案
从前面的分析得知,要想解决上述问题,必须在拉取依赖包时能屏蔽某些不想使用的仓库地址。为此,我们总结了三种方案,供大家参考。
方案一 设定连接超时时间
我们知道 maven 底层是通过 http client 来拉取数据的,一定是有连接建立超时时间或者数据读取时间限制,那么对于想要屏蔽的地址,只要我们将该时间设置的尽可能短,那么就可以达到忽略某些仓库地址的目的。
比如,对于已经下线的 maven-yuceyi 和 clubfactory 仓库,我们在 settings.xml 中进行如下设置(参考官方文档):
那么对于已经下线的旧仓库地址来说,在 1000ms 内就会连接失败,可以解决一直卡住的问题,如下图所示:
这里已经可以解决旧仓库地址无法连接导致卡住的问题,但是不太优雅,因为构建日志中会出现很多异常日志。那么有没有既可以屏蔽旧仓库地址,又不引入异常日志的方案呢?答案是肯定的,这就是接下来介绍的方案二。
方案二 使用 mirrorOf 机制
我们知道,在 maven 的 settings.xml 配置中,有一个 mirror 配置,用来把某些仓库的请求都由指定的仓库地址进行代理(参考官方文档)。那么对于已经下线的旧仓库地址来说,只要我们把它们配置为使用新仓库地址进行 mirror,就实现了屏蔽某些仓库的目的,并且不会出现异常,因为不会向旧地址发起请求。
mirror 配置如下:
其中 maven-publics 是我们新仓库地址的 id,maven-yuceyi, clubfactory 是旧仓库地址的 id。如果想要用新仓库地址代理所有仓库请求,那么设置为 <mirrorOf>*</mirrorOf>
即可。
方案三 扩展 maven-resolver-impl 实现忽略特定仓库
对于解决旧仓库地址问题,使用 mirrorOf 已经足够,但是我们希望拓展一下思路,能不能让 maven 通过某些参数来指定在构建时忽略特定仓库,这样操作起来将更加方便,扩展性也更强。
为此,我在网上搜寻了好久,包括也去 maven 官网看了,都没有找到类似的配置。于是就有了想法,自己改造一下,支持这个小功能。
从 DEBUG 日志入手,找到了在下载依赖时所处理的逻辑是在 maven-resolver-impl
这个 jar 包,而这个 jar 包是在 Maven Artifact Resolver 这个开源项目下(github镜像),因此,只要实现逻辑重新打包,最后替换掉 ${M2_HOME}/lib/下的 maven-resolver-impl-xxx.jar 即可。
实现逻辑很简单,就是支持系统属性或者环境变量配置忽略的仓库 id 列表,在下载 jar 包或者更新 metadata 信息时不使用忽略的仓库即可。
step1 下载源码
下载 maven-resolver 项目源码,切换到 maven-resolver-1.6.x 分支。
这里需要注意下版本兼容问题,我使用的是 3.8.2 版本 maven,依赖的是 maven-resolver-impl-1.6.3.jar,所以我选择给予 1.6.x 分支进行扩展。
step2 增加定制逻辑
找到需要扩展逻辑的类org.eclipse.aether.internal.impl.DefaultArtifactResolver
以及 org.eclipse.aether.internal.impl.DefaultMetadataResolver
在 DefaultArtifactResolver 类的 performDownloads()方法中添加如下代码:
在 DefaultMetadataResolver 类的 ResolveTask 的 run()方法中添加以下代码:
其中 Utils.getIgnoredRepositories()代码如下:
下面就是重新打包(这里需要注意下,maven 项目有一致的 Check-style 检查,打包如果出现 check-style 失败,仔细按提示修改下即可),然后拷贝到 ${M2_HOME}/lib/下即可,如下图所示:
tips:
这里建议把原始 maven-resolver-impl.jar 包备份,以免出现问题需要回滚。
step 3 验证
这一步验证要求将 settings.xml 的 mirrorOf 配置恢复为只 mirrorOf central,避免造成影响。
执行打包命令(-D 指定系统属性):
或者使用环境变量:
此时查看 debug 日志会发现,配置的仓库列表确实被忽略,如下所示:
这里我们细心注意下图中的日志会发现,在下载正式包时,会根据候选仓库列表(clubfactory, maven-publics, maven-yuceyi)依次进行尝试,直到下载成功。上图中 clubfactory 仓库被忽略,直接跳过,尝试从 maven-publics 下载,下载成功后即不再尝试 maven-yuceyi 仓库,所以从图中无法看出打印忽略 maven-yuceyi 仓库的日志。
而上图在下载 SNAPSHOT 包时(或者构建时强制指定 -U 选项),会向所有候选仓库发送下载请求/更新 metadata 请求(这是为了保证本地拉取到的 SNAPSHOT 包是所有仓库中最新的),这时可以看到 clubfactory 仓库、maven-yuceyi 仓库都被忽略了,只有 maven-publics 仓库下载成功。
至此,我们通过扩展 maven-resolver-impl 实现了一个新功能:可以通过系统属性或者环境变量指定忽略某些仓库。
各解决方案比较
从上述解决忽略指定仓库问题的方案来看,各有各的特点,大家可以根据具体情况选择使用。
方案一 设置连接超时
这个方案操作简单,但没有根本解决问题,maven 仍然尝试连接并且会有很多异常日志出现。
方案二 使用 mirrorOf 机制
该方案可以根本解决问题,尤其对于很多使用单个仓库作为全公司私服的情况,使用 mirrorOf * 尤其方便,唯一的缺点是频繁修改 settings.xml 文件还是挺累人。
方案三 扩展 maven-resolver-impl
这个方案增加了新功能,支持通过系统属性或者环境变量来指定忽略某些仓库,使用简单方便,扩展性好,缺点是需要自行实现新功能并替换 maven 依赖的 jar 包,并且在升级 maven 版本时很容易丢失自定义 jar 包。
综上所述,方案二和方案三都可以采用,对于在公共机器上进行构建的场景,建议使用方案二,对于自己本地打包的情况,建议使用方案三。
总结与思考
本文针对 maven 私服迁移过程中出现的旧仓库地址失效需要屏蔽的情况,给出了三种解决方案并分析了其优缺点,希望能给有类似需求的读者以参考。
另外,针对在推往私服的 jar 包中指定仓库地址的用法,个人表示不太赞成,也不符合最佳实践,因为如果出现仓库地址迁移,不可避免会遇到拉取依赖卡住的问题,给依赖方造成不必要的麻烦。当然,这里更是给大家提了醒,拷贝 maven 配置时一定要弄清楚来龙去脉,否则一旦出现问题就是“窝案”。
大家对这个问题有什么看法,或者遇到类似问题有其他解决方案,欢迎留言交流~
版权声明: 本文为 InfoQ 作者【小江】的原创文章。
原文链接:【http://xie.infoq.cn/article/f31db47123110525c6861050d】。文章转载请联系作者。
评论