加快Spring Boot启动时间

时间:2014-12-01 14:37:06

标签: java performance spring-boot startup

我有一个Spring Boot应用程序。我添加了很多依赖项(不幸的是,看起来我需要所有这些依赖项)并且启动时间上升了很多。只做SpringApplication.run(source, args)需要10秒钟。

虽然与“使用”的内容相比可能没那么多,但我很不高兴它需要那么多,主要是因为它打破了开发流程。此时应用程序本身相当小,所以我假设大部分时间都与添加的依赖项有关,而不是应用程序类本身。

我认为问题是类路径扫描,但我不确定如何:

  • 确认是问题(即如何“调试”Spring Boot)
  • 如果真的是原因,我怎么能限制它,所以它变得更快?例如,如果我知道某些依赖项或包不包含Spring应扫描的任何内容,是否有办法限制它?

我认为enhancing Spring to have parallel bean initialization during startup可以加快速度,但是自2011年以来,该增强请求已经开放,没有任何进展。我在Spring Boot本身看到了一些其他的努力,例如Investigate Tomcat JarScanning speed improvements,但这是特定于Tomcat的,并且已被放弃。

这篇文章:

虽然针对集成测试,建议使用lazy-init=true,但我不知道如何使用Java配置将此应用于Spring Boot中的所有bean - 这里有任何指针吗?

欢迎任何(其他)建议。

10 个答案:

答案 0 :(得分:44)

Spring Boot会执行许多可能不需要的自动配置。因此,您可能只想缩小应用程序所需的自动配置范围。要查看包含的自动配置的完整列表,只需在DEBUG模式下运行org.springframework.boot.autoconfigure的日志记录(logging.level.org.springframework.boot.autoconfigure=DEBUG中的application.properties)。另一个选项是使用--debug选项运行spring boot应用程序:java -jar myproject-0.0.1-SNAPSHOT.jar --debug

输出中会出现类似的情况:

=========================
AUTO-CONFIGURATION REPORT
=========================

检查此列表并仅包含您需要的自动配置:

@Configuration
@Import({
        DispatcherServletAutoConfiguration.class,
        EmbeddedServletContainerAutoConfiguration.class,
        ErrorMvcAutoConfiguration.class,
        HttpEncodingAutoConfiguration.class,
        HttpMessageConvertersAutoConfiguration.class,
        JacksonAutoConfiguration.class,
        ServerPropertiesAutoConfiguration.class,
        PropertyPlaceholderAutoConfiguration.class,
        ThymeleafAutoConfiguration.class,
        WebMvcAutoConfiguration.class,
        WebSocketAutoConfiguration.class,
})
public class SampleWebUiApplication {

代码是从this blog post复制的。

答案 1 :(得分:22)

到目前为止,投票最多的答案并没有错,但它没有深入到我喜欢的深度,也没有提供任何科学证据。 Spring Boot团队进行了一项练习,以减少Boot 2.0的启动时间,并且票证11226包含许多有用的信息。还有一个票据7939可以向条件评估添加时间信息,但它似乎没有特定的ETA。

调试启动启动的最有用,最有条理的方法是由Dave Syer完成的。 https://github.com/dsyer/spring-boot-startup-bench

我也有一个类似的用例,所以我采用了Dave与JMH进行微基准测试的方法并运行它。结果是boot-benchmark项目。我设计它可以用来测量任何Spring Boot应用程序的启动时间,使用bootJar(之前在Boot 1.5中称为bootRepackage)Gradle任务生成的可执行jar。随意使用它并提供反馈。

我的调查结果如下:

  1. CPU很重要。很多。
  2. 使用-Xverify:none启动JVM非常有帮助。
  3. 排除不必要的自动配置会有所帮助。
  4. Dave推荐JVM参数-XX:TieredStopAtLevel=1,但我的测试没有显示出明显的改进。此外,-XX:TieredStopAtLevel=1可能会减慢您的第一次请求。
  5. 主机名解析速度reports很慢,但我发现我测试的应用程序没有问题。

答案 2 :(得分:6)

正如本问题/答案中所述,我认为最好的方法是不要只添加您认为需要的那些,而是排除您认为不需要的依赖项。

请参阅:Minimise Spring Boot Startup Time

总结:

您可以查看正在进行的操作,并启用调试日志记录,就像从命令行启动应用程序时指定--debug一样简单。您还可以在application.properties中指定debug = true。

此外,您可以将application.properties中的日志记录级别设置为:

logging.level.org.springframework.web:DEBUG logging.level.org.hibernate:错误

如果检测到您不想要的自动配置模块,则可以将其禁用。可在此处找到相关文档:http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration

示例如下:

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}

答案 3 :(得分:4)

Spring Boot 2.2.M1 添加了支持Spring Boot中的惰性初始化的功能。

默认情况下,刷新应用程序上下文时,将创建上下文中的每个bean并注入其依赖项。相比之下,当将bean定义配置为延迟初始化时,将不会创建它,并且直到需要时才注入依赖项。

启用延迟初始化:将spring.main.lazy-initialization设置为 true

有关更多详细信息,请检查Doc

答案 4 :(得分:4)

这里有完整的可能操作列表:https://spring.io/blog/2018/12/12/how-fast-is-spring

我将在Spring方面进行一些最重要的说明(稍​​作调整):

    Spring Boot Web入门中的
  • Classpath排除项:
    • 休眠验证器
    • Jackson(但Spring Boot执行器取决于它)。如果您需要JSON呈现,请使用Gson(仅与MVC配合使用)。
    • 登录:使用slf4j-jdk14代替
  • 使用spring-context-indexer。它不会增加太多,但会有所帮助。
  • 如果您负担不起,请不要使用执行器。
  • 使用Spring Boot 2.1和Spring 5.1。可用时切换到2.2和5.2。
  • 使用spring.config.location(命令行参数或系统属性等)固定Spring Boot配置文件的位置。在IDE中进行测试的示例:spring.config.location=file://./src/main/resources/application.properties
  • 如果您不需要spring.jmx.enabled=false,请关闭JMX(这是Spring Boot 2.2中的默认设置)
  • 默认情况下使bean定义变懒。 Spring Boot 2.2中有一个新标记spring.main.lazy-initialization=true(对于较旧的Spring使用LazyInitBeanFactoryPostProcessor)。
  • 解开胖子jar并使用显式类路径运行。
  • 使用-noverify运行JVM。还考虑使用-XX:TieredStopAtLevel=1(稍后会降低JIT速度,但会节省启动时间)。

提到的LazyInitBeanFactoryPostProcessor(如果无法应用Spring 2.2中可用的标记spring.main.lazy-initialization=true,则可以在Spring 1.5中使用它:

public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
      for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        definition.setLazyInit(true);
      }
  }
}

您还可以使用(或编写自己的-很简单)来分析bean的初始化时间:https://github.com/lwaddicor/spring-startup-analysis

希望有帮助!

答案 5 :(得分:-1)

如果您要优化手动测试的开发周转时间,强烈建议使用devtools

  

使用spring-boot-devtools的应用程序将自动重启   每当类路径上的文件更改时。

只需重新编译-服务器将自行重启(对于Groovy,您只需要更新源文件)。如果您使用的是IDE(例如“ vscode”),它可能会自动编译Java文件,因此,仅保存Java文件就可以间接启动服务器重启-在这方面,Java与Groovy一样无缝。 / p>

这种方法的优点在于,增量重新启动会短路一些从头开始的启动步骤-这样您的服务将被备份并更快地运行!


不幸的是,这对部署或自动单元测试的启动时间没有帮助。

答案 6 :(得分:-1)

就我而言,断点太多。当我单击“静音断点”并以调试模式重新启动应用程序时,应用程序启动速度提高了10倍。

答案 7 :(得分:-2)

警告:如果您不使用Hibernate DDL来自动生成数据库模式,也不使用L2缓存,则此答案不适用于您。向前滚动。

我的发现是Hibernate为应用程序启动增加了大量时间。禁用L2缓存和database initialization可加快Spring Boot应用程序的启动速度。将缓存保留为生产状态,并在开发环境中将其禁用。

application.yml:

spring:
  jpa:
    generate-ddl: false
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        cache:
          use_second_level_cache: false
          use_query_cache: false

测试结果:

  1. L2缓存已打开,并且ddl-auto: update

    INFO 5024 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 23331 ms
    INFO 5024 --- [restartedMain] b.n.spring.Application : Started Application in 54.251 seconds (JVM running for 63.766)
    
  2. L2缓存已关闭,并且ddl-auto: none

    INFO 10288 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 9863 ms
    INFO 10288 --- [restartedMain] b.n.spring.Application : Started Application in 32.058 seconds (JVM running for 37.625)
    

现在我想知道所有这些空闲时间该怎么办

答案 8 :(得分:-3)

我觉得很奇怪,以前没有人建议过这些优化。以下是在开发时优化项目构建和启动的一些一般技巧:

  • 从防病毒扫描程序中排除开发目录:
    • 项目目录
    • 构建输出目录(如果在项目目录之外)
    • IDE索引目录(例如〜/ .IntelliJIdea2018.3)
    • 部署目录(Tomcat中的Web应用程序)
  • 升级硬件。使用更快的CPU和RAM,更好的Internet连接(用于下载依赖项)和数据库连接,切换到SSD。显卡没关系。

警告

  1. 第一个选择是以降低安全性为代价的。
  2. 第二种选择显然要花钱。

答案 9 :(得分:-6)

对我而言,听起来你使用了错误的配置设置。 首先检查myContainer和可能的冲突。 要确定谁在使用最多的资源,每次检查每个依赖项的内存映射(请参阅数据量!) - 这需要大量时间,以及......(以及SUDO权限)。 顺便问一下:您是否经常针对依赖项测试代码?