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

544 阅读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秒。

环境配置(十次平均值)启动速度
springboot2+jdk1.8≈10.3s
延迟初始化Bean≈8.63s

二、创建扫描索引

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
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

环境配置(十次平均值)启动速度
springboot2+jdk1.8≈10.3s
+延迟初始化Bean≈8.63s
+创建扫描索引≈7.7s

其他技巧:

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 的应用程序编译成本地可执行的镜像文件,可以显著提升启动速度、峰值性能以及减少内存使用。


最后,推荐一款应用开发神器

扯个嗓子!关于目前低代码在技术领域很活跃!

低代码是什么?一组数字技术工具平台,能基于图形化拖拽、参数化配置等更为高效的方式,实现快速构建、数据编排、连接生态、中台服务等。通过少量代码或不用代码实现数字化转型中的场景应用创新。它能缓解甚至解决庞大的市场需求与传统的开发生产力引发的供需关系矛盾问题,是数字化转型过程中降本增效趋势下的产物。

这边介绍一款好用的低代码平台——JNPF快速开发平台。近年在市场表现和产品竞争力方面表现较为突出,采用的是最新主流前后分离框架(SpringBoot+Mybatis-plus+Ant-Design+Vue3)。代码生成器依赖性低,灵活的扩展能力,可灵活实现二次开发。

以JNPF为代表的企业级低代码平台为了支撑更高技术要求的应用开发,从数据库建模、Web API构建到页面设计,与传统软件开发几乎没有差异,只是通过低代码可视化模式,减少了构建“增删改查”功能的重复劳动,还没有了解过低代码的伙伴可以尝试了解一下。

应用:www.jnpfsoft.com/?juejin

有了它,开发人员在开发过程中就可以轻松上手,充分利用传统开发模式下积累的经验。所以低代码平台对于程序员来说,有着很大帮助。