经过一顿骚操作,项目编译打包速度提升一倍!

Posted woshidanzi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了经过一顿骚操作,项目编译打包速度提升一倍!相关的知识,希望对你有一定的参考价值。

我们的公司的系统中是以微服务方式开发的。每个项目就是一个微服务,有非常多的微服务,比如用户服务、后台管理服务、订单服务、物流服务、商品服务等等。

其中每个项目都使用的 gradle 构建工具打包、发布、部署。

gradle 是一个项目构建工具,与 maven 类似,它提供非常强大的包依赖管理功能。

现象与问题

  1. 有些项目打包编译速度非常慢,有时需要 5 到 10 分钟,而且还经常失败。
  2. 有时候点击三方包的查看源码,通过 IDE 进入类的 class 文件,点击下载源码,控制台显示失败。
  3. 项目发布的 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 配置,会发现一些问题:

  1. allprojects 配置中配置了很多的依赖包,并且采用了 io.spring.dependency-management、org.springframework.boot 这两个插件,来管理依赖,并且设置了依赖管理器 dependencyManagement 配置,这就导致了要管理所有模块的依赖包。

我们去掉 allprojects 中的 io.spring.dependency-management、org.springframework.boot插件,以及 dependencyManagement 配置。修改某些需要被依赖的 jar 包指定固定版本号。

  1. 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 包
        ...
    }
    ...其他配置...
}

...等等模块...

对项目重新进行编译打包,会发现速度非常快,丝滑流畅~

总结

  1. 通过优化上述配置,更加深刻的理解了 gradle 配置,以及它的依赖管理;
  2. 遇到类似的问题,不要慌,在执行 gradle 打包命编译令时增加 debug、stack 参数,可以输出更加多有用的信息,帮助我们排查问题;
  3. 项目中的依赖很多的很复杂的时候,需要花时间梳理配置信息,删繁就简,整理出一个大体框架,更好的帮助我们理解与分析。
 

以上是关于经过一顿骚操作,项目编译打包速度提升一倍!的主要内容,如果未能解决你的问题,请参考以下文章

vue-cli3使用 DllPlugin 实现预编译,提升构建速度

Xcode编译慢打包慢解决方法

webpack5高级优化——提升打包速度

快速排序

深度学习-数据加载优化-训练速度提升一倍

深度学习-数据加载优化-训练速度提升一倍