写点什么

运维让我优化 SpringBoot 启动速度,我是这么干的!

作者:高端章鱼哥
  • 2023-08-01
    福建
  • 本文字数:1625 字

    阅读完需:约 5 分钟

Spring Boot 毫无疑问是 Java 后端开发的第一大框架,基于 Spring Boot 有着一套完整的工具链,各种各样的 starter。对于日常业务开发而言,可以说是轮子很全。


但随着微服务和云原生时代的流行,Spring Boot 应用却暴露出了一些问题,其中比较突出的有:

  • 启动慢

  • 应用内存占用多

  • 云原生应用对启动速度的要求比较高。当需要进行水平扩展时,要求这些新的实例必须在足够短的时间内完成启动,从而尽快的处理新增的请求。

  • 云原生应用要求在运行时占用尽可能少的资源。尽可能的减少单个实例占用的资源,就意味着可以用同样的成本,支持更多的访问请求。

  • 云原生应用要求更小的打包体积。云原生应用以容器镜像的形式打包。应用镜像的尺寸越大,所需要的存储空间也会越大,推送和拉取镜像所耗费的时间也会更长。


其实我们都比较清楚大部分的启动时间是由于 Spring 需要加载各种 Bean 导致启动速度下降的

一、延迟初始化 Bean


一般在 SpringBoot 中都拥有很多的耗时任务,比如数据库建立连接、初始线程池的创建等等,我们可以延迟这些操作的初始化,来达到优化启动速度的目的。Spring Boot 2.2 版本后引入 spring.main.lazy-initialization 属性,配置为 true 会将所有 Bean 延迟初始化。


spring:  main:    lazy-initialization: true
复制代码


个人本地开启延迟初始化之后,启动能快了 1~2 秒。


二、创建扫描索引


Spring5 之后提供了 spring-context-indexer 功能,可以通过在编译时创建一个静态候选列表来提高大型应用程序的启动性能。

先看官方的解释:



在项目中使用了 @Indexed 之后,编译打包的时候会在项目中自动生成 META-INT/spring.components 文件。


当 Spring 应用上下文执行 ComponentScan 扫描时,META-INT/spring.components 将会被 CandidateComponentsIndexLoader 读取并加载,转换为 CandidateComponentsIndex 对象,这样的话 @ComponentScan 不在扫描指定的 package,而是读取 CandidateComponentsIndex 对象,从而达到提升性能的目的。


我们只需要将依赖引入,然后在启动类上使用 @Indexed 注解即可。这样在程序编译打包之后会生成 META-INT/spring.components 文件,当执行 @ComponentScan 扫描类时,会读取索引文件,提高扫描速度。


<dependency>  	<groupId>org.springframework</groupId>  	<artifactId>spring-context-indexer</artifactId>  	<optional>true</optional></dependency>
复制代码


@Indexed@SpringBootApplicationpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}
复制代码



其他技巧:

1、减少 @ComponentScan @SpringBootApplication 扫描类时候的范围

2、关闭 Spring Boot 的 JMX 监控,设置 spring.jmx.enabled=false

3、设置 JVM 参数 -noverify ,不对类进行验证

4、对非必要启动时加载的 Bean,延迟加载 5、使用 Spring Boot 的全局懒加载一

5、AOPQ 切面尽量不使用注解方式,这会导致启动时扫描全部方法 7、关闭 endpoint 的一些监控功能

6、排除项目多余的依赖 jar

7、swagger 扫描接口时,指定只扫描某个路径下的类 10、Feign 客户端接口的扫描缩小包扫描范围

到这启动速度应该算是优化的比较极致了, 但是内存占用大依然是问题

三、 升级 jdk17


当然 jdk 也在这方面做了很大的努力:

内存占用多主要是内存占用后不会归还操作系统,这个正在逐步改善:

  • G1 JDK12 及之后 已支持

  • ZGC JDK13 及之后 已支持

由于 Java 语言的特性及 Spring Boot 的一些实现方式,决定了即便是开启了 G1/ZGC 的未使用内存及时归还操作系统,Spring Boot 的内存占用,仍然远大于 Golang 这种编译型语言。

所以,Java 想要解决云原生时代的问题,目前的方案基本都是基于 GraalVM 来的,不管是 Quarkus(夸克)还是 Micronaut 都是。

那么,Spring Boot 有没有类似的方案呢?:spring-graalvm-native

四、升级 SpringBoot3


spring-graalvm-native 是 springBoo6/SpringBoot3 非常重大的一个特性,支持使用 GraalVM 将 SpringBoot 的应用程序编译成本地可执行的镜像文件,可以显著提升启动速度、峰值性能以及减少内存使用。

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
运维让我优化SpringBoot启动速度,我是这么干的!_运维_高端章鱼哥_InfoQ写作社区