我们的公司的系统中是以微服务方式开发的。每个项目就是一个微服务,有非常多的微服务,比如用户服务、后台管理服务、订单服务、物流服务、商品服务等等。
其中每个项目都使用的 gradle 构建工具打包、发布、部署。
gradle 是一个项目构建工具,与 maven 类似,它提供非常强大的包依赖管理功能。
现象与问题
- 有些项目打包编译速度非常慢,有时需要 5 到 10 分钟,而且还经常失败。
- 有时候点击三方包的查看源码,通过 IDE 进入类的 class 文件,点击下载源码,控制台显示失败。
- 项目发布的 api 包中,除了 rpc 接口必须的 model,还增加了很多无用的包,导致其他项目引入后,jar 过多。
分析
带着这三个问题开始我们的分析。
问题一:项目打包编译速度非常慢,并且经常失败
通过查看打包日志,发现失败的信息是,编译某个模块时时间过长、通过 maven 仓库下载包的时候经常失败、显示超时信息等等,最终输出不能解析 xxx.xxx.xxx 包。
然后再次重新打包,又经过一段时间漫长的等待,可能会成功,但是时间很长。
在打包时增加 debug 选项查看下打包过程中出现错误的日志信息。
我们发现,项目模块在编译打包的时候,出现访问远程仓库资源 403 的信息。
00:29:59.125 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Using com.xgoods:xabtest-proto:v0.3.0 from Maven repository ‘nexus‘
00:29:59.125 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Discarding resolve failure.
org.gradle.internal.resolve.ModuleVersionResolveException: Failed to list versions for com.xgoods:xabtest-proto.
at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingModuleComponentRepository$ErrorHandlingModuleComponentRepositoryAccess.listModuleVersions(ErrorHandlingModuleComponentRepository.java:126)
...省略...
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.gradle.api.resources.ResourceException: Unable to load Maven meta-data from http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml.
at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.load(MavenMetadataLoader.java:55)
at org.gradle.api.internal.artifacts.repositories.maven.MavenVersionLister.listVersions(MavenVersionLister.java:48)
at org.gradle.api.internal.artifacts.repositories.metadata.DefaultMavenPomMetadataSource.listModuleVersions(DefaultMavenPomMetadataSource.java:73)
at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver.doListModuleVersions(ExternalResourceResolver.java:210)
at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver.access$200(ExternalResourceResolver.java:90)
at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver$RemoteRepositoryAccess.listModuleVersions(ExternalResourceResolver.java:440)
at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.CachingModuleComponentRepository$ResolveAndCacheRepositoryAccess.listModuleVersions(CachingModuleComponentRepository.java:360)
at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingModuleComponentRepository$ErrorHandlingModuleComponentRepositoryAccess.listModuleVersions(ErrorHandlingModuleComponentRepository.java:121)
... 107 more
Caused by: org.gradle.api.resources.ResourceException: Could not get resource ‘http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml‘.
at org.gradle.internal.resource.ResourceExceptions.failure(ResourceExceptions.java:74)
at org.gradle.internal.resource.ResourceExceptions.getFailed(ResourceExceptions.java:57)
at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.copyToCache(DefaultCacheAwareExternalResourceAccessor.java:201)
at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.access$300(DefaultCacheAwareExternalResourceAccessor.java:54)
at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor$1.create(DefaultCacheAwareExternalResourceAccessor.java:89)
at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor$1.create(DefaultCacheAwareExternalResourceAccessor.java:81)
at org.gradle.cache.internal.ProducerGuard$AdaptiveProducerGuard.guardByKey(ProducerGuard.java:97)
at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.getResource(DefaultCacheAwareExternalResourceAccessor.java:81)
at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.parseMavenMetadataInfo(MavenMetadataLoader.java:61)
at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.load(MavenMetadataLoader.java:51)
... 114 more
Caused by: org.gradle.internal.resource.transport.http.HttpErrorStatusCodeException: Could not GET ‘http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml‘. Received status code 403 from server: Forbidden
at org.gradle.internal.resource.transport.http.HttpClientHelper.processResponse(HttpClientHelper.java:158)
at org.gradle.internal.resource.transport.http.HttpClientHelper.performGet(HttpClientHelper.java:84)
at org.gradle.internal.resource.transport.http.HttpResourceAccessor.openResource(HttpResourceAccessor.java:43)
at org.gradle.internal.resource.transport.http.HttpResourceAccessor.openResource(HttpResourceAccessor.java:29)
at org.gradle.internal.resource.transfer.DefaultExternalResourceConnector.openResource(DefaultExternalResourceConnector.java:56)
at org.gradle.internal.resource.transfer.ProgressLoggingExternalResourceAccessor.openResource(ProgressLoggingExternalResourceAccessor.java:37)
at org.gradle.internal.resource.transfer.AccessorBackedExternalResource.withContentIfPresent(AccessorBackedExternalResource.java:130)
at org.gradle.internal.resource.BuildOperationFiringExternalResourceDecorator$11.call(BuildOperationFiringExternalResourceDecorator.java:237)
我们发现,项目模块在编译打包的时候,一些 jar 原本是只是会存在我们公司私服上的的,但是却跑到了其他的远程仓库上下载,然后就出现访问远程仓库资源 403 的信息,因为 jar 只有在私服上,所以远程仓库肯定是没有的!
上述情况在打包编译的时候经常发生,原因就是一些 jar 包属于公司私服上的,下载 jar 包时,应该在私服下载,但是却一直在远程仓库下载,导致多次选择不同仓库重试下载,一定时间之后出现了超时、失败信息。
现在来查看项目的 build.gradle 文件结构。
// 公司的公用 gradle 插件
apply from: "http://xxx.xxx.look.gradle"
buildscript {
apply from: "http://xxx.xxx.look.gradle"
repositories {
mavenLocal()
mavenCentral()
maven { url "http://repo.spring.io/release" }
maven { url ‘https://repo.spring.io/libs-milestone‘ }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/snapshot" }
}
// ...一些配置...
}
// project setting
allprojects {
apply plugin: "idea"
apply plugin: "io.spring.dependency-management"
apply plugin: "org.springframework.boot"
repositories {
maven {
name "atlassian-public"
url "https://m2proxy.atlassian.com/repository/public"
}
mavenLocal()
mavenCentral()
maven { url "http://repo.spring.io/release" }
maven { url ‘https://repo.spring.io/libs-milestone‘ }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/snapshot" }
maven { url ‘http://maven.youzanyun.com/repository/maven-releases/‘ }
}
// 依赖管理器
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
// ...各种配置...
dependencies {
compileOnly ‘org.projectlombok:lombok:1.18.10‘
annotationProcessor ‘org.projectlombok:lombok:1.18.10‘
// ...依赖超级多的 jar 包,包括公司的包和三方包....
compile "xxx.xxx.xxx"
...
}
}
// 模块一
project(‘:xeshop-common‘) {
dependencies {
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块二
project(‘:xeshop-dao‘) {
dependencies {
// 依赖模块一
compile project(‘:xeshop-common‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块三
project(‘:xeshop-service‘) {
dependencies {
// 依赖模块二
compile project(‘:xeshop-dao‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块四
project(‘:xeshop-thirdbiz‘) {
dependencies {
// 依赖模块三
compile project(‘:xeshop-service‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块五
project(‘:xeshop-rpc‘) {
dependencies {
// 依赖模块三
compile project(‘:xeshop-service‘)
// 依赖模块四
compile project(‘:xeshop-thirdbiz‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块六
project(‘:xeshop-api‘) {
dependencies {
// 依赖模块三
compile project(‘:xeshop-service‘)
// 依赖模块四
compile project(‘:xeshop-thirdbiz‘)
// 依赖模块五
compile project(‘:xeshop-rpc‘)
// 依赖一些模块 和 一堆 jar 包
...
}
...其他配置...
}
...等等模块...
通过查看上面的 build.gradle 配置,会发现一些问题:
- allprojects 配置中配置了很多的依赖包,并且采用了 io.spring.dependency-management、org.springframework.boot 这两个插件,来管理依赖,并且设置了依赖管理器 dependencyManagement 配置,这就导致了要管理所有模块的依赖包。
我们去掉 allprojects 中的 io.spring.dependency-management、org.springframework.boot插件,以及 dependencyManagement 配置。修改某些需要被依赖的 jar 包指定固定版本号。
- allprojects 配置了仓库地址,但是 buildscript 已经配置了相关的仓库地址;
我们去掉 allprojects 中的 repositories 配置。
问题二:下载三方源码包失败
下载源码的操作如下。
异常信息是:
Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2.
Searched in the following locations:
- file:/xxx.xxxx/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Greenwich.SR2/spring-cloud-dependencies-Greenwich.SR2.pom
Required by:
project :xeshop-common
这个异常信息是说,在 /xxx.xxxx/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Greenwich.SR2/spring-cloud-dependencies-Greenwich.SR2.pom 这个地址上找不到 org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2 包,需要 xeshop-common 项目。
我们查看 build.gradle 文件,发现allprojects 配置中,配置依赖管理类,如下配置:
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2"
}
}
mavenBom 是为了防止用Maven管理Spring项目时,不同的项目依赖了不同版本的Spring,可以使用Maven BOM来解决者一问题。
但是在 allprojects 配置中配置,会导致所有的项目的源码都要经过它来管理下载。
我们把它注释掉。
问题三、各个模块之间的依赖有些重复
将 allprojects 中,除了一些必须全部模块公用的jar,其他的 jar 全部移除,并且放入 xeshop-common 依赖中。
解决
通过上面的分析,我们修改后的 build.gradle 如下配置:
// 公用 gradle 插件
apply from: "http://www.yixingylzc.cn xxx.xxx.look.gradle"
buildscript {
apply from: "http://www.shbkrcxzz.cn xxx.xxx.look.gradle"
repositories {
mavenLocal()
mavenCentral()
maven { url "http://repo.spring.io/release" }
maven { url ‘https://repo.spring.io/libs-milestone‘ }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/snapshot" }
}
// ...一些配置...
}
// project setting
allprojects {
apply plugin: "idea"
// ...各种配置...
dependencies {
// 这里只留一些必要的公共包,其他的包全部移动到 xeshop-common 模块中
...
}
}
// 模块一
project(‘:xeshop-common‘) {
dependencies {
// 添加依赖包
...
}
}
// 模块二
project(‘:xeshop-dao‘) {
dependencies {
// 依赖模块一
compile project(‘一品娱乐:www.lanboyulezc.cn :xeshop-common‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块三
project(‘:xeshop-service‘) {
dependencies {
// 依赖模块二
compile project(‘:xeshop-dao‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块四
project(‘:xeshop-thirdbiz‘) {
dependencies {
// 依赖模块三
compile project(‘:xeshop-service‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块五
project(‘:xeshop-rpc‘) 优亿在线:www.jintianxuesha.com{
dependencies {
// 依赖模块四
compile project(‘:xeshop-thirdbiz‘)
// 依赖一些模块 和 一堆 jar 包
...
}
}
// 模块六
project(‘:xeshop-api‘) {
dependencies {
// 依赖模块五
compile project(‘:xeshop-rpc‘)
// 依赖一些模块 和 百事2:www.baihuayl7.cn 一堆 jar 包
...
}
...其他配置...
}
...等等模块...
对项目重新进行编译打包,会发现速度非常快,丝滑流畅~
总结
- 通过优化上述配置,更加深刻的理解了 gradle 配置,以及它的依赖管理;
- 遇到类似的问题,不要慌,在执行 gradle 打包命编译令时增加 debug、stack 参数,可以输出更加多有用的信息,帮助我们排查问题;
- 项目中的依赖很多的很复杂的时候,需要花时间梳理配置信息,删繁就简,整理出一个大体框架,更好的帮助我们理解与分析。