Spring Bootの起動時間を短縮する


114

Spring Bootアプリケーションがあります。多くの依存関係を追加しましたが(残念ながら、すべての依存関係が必要です)、起動時間がかなり長くなりました。a SpringApplication.run(source, args)を実行するだけで10秒かかります。

これは「慣れている」ものと比べてそれほど多くはないかもしれませんが、それが開発フローを壊してしまうために、それだけかかることは不満です。この時点ではアプリケーション自体はかなり小さいので、ほとんどの場合、アプリケーションクラス自体ではなく、追加された依存関係に関連していると思います。

問題はクラスパススキャンであると思いますが、次の方法がわかりません。

  • それが問題であることを確認します(つまり、Spring Bootを「デバッグ」する方法)
  • それが本当に原因である場合、どうすれば制限できるので、速くなりますか?たとえば、Springがスキャンする必要のあるものが依存関係またはパッケージに含まれていないことがわかっている場合、それを制限する方法はありますか?

私がいることを前提とし、起動時にパラレル豆の初期化を持って春を強化することは、物事をスピードアップするだろうが、その機能拡張要求は、任意の進行せず、2011年以来開いてきました。Investigate Tomcat JarScanningの速度の改善など、Spring Boot自体にもいくつかの取り組みが見られますが、それはTomcat固有のものであり、放棄されています。

この記事:

統合テストを目的としていますが、の使用を推奨しlazy-init=trueていますが、これをJava構成を使用してSpring BootのすべてのBeanに適用する方法がわかりません-ここにポインタがありますか?

(その他の)提案があれば歓迎します。


コードを投稿します。通常は、アプリケーションランナーが定義されているパッケージのみがスキャンされます。他に定義され@ComponentScanているパッケージがある場合、それらもスキャンされます。もう1つのことは、一般にロギングが非常に遅いため、デバッグまたはトレースロギングを有効にしていないことを確認することです。
M. Deinum 14

Hibernateを使用する場合、アプリケーションの起動時にかなりの時間を消費する傾向もあります。
Knut Forkalsrud 2015年

Springのタイプ別の自動バインドとファクトリーBeanを組み合わせると、多くのBeanと依存関係を追加するのが遅くなる可能性があります。
Knut Forkalsrud 2015年


2
コメントありがとうございました-残念ながら、コードを投稿することはできません(多くの内部jar)。しかし、これをデバッグする方法をまだ探しています。はい、AまたはBを使用しているか、XまたはYを実行しているため、速度が低下しています。これをどのように判断しますか?15個の推移的な依存関係がある依存関係Xを追加した場合、それらの16個のうちどれが遅くなったのかをどうやって知るのですか?見つけることができた場合、Springがそれらを調べるのを防ぐために後で何かできることはありますか?そのようなポインターは役に立ちます!
安定した雨

回答:


61

Spring Bootは必要のない多くの自動設定を行います。そのため、アプリに必要な自動構成のみを絞り込むことができます。付属の自動設定の完全なリストを表示するには、ただのロギングを実行してorg.springframework.boot.autoconfigureデバッグモード(中logging.level.org.springframework.boot.autoconfigure=DEBUGapplication.properties)。別のオプションは、オプションで春のブートアプリケーションを実行することです--debugjava -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 {

コードはこのブログ投稿からコピーされました。


1
これを測定しましたか?ずっと速かった?私の意見では、これは例外的なケースであり、Springテストコンテキストキャッシュが機能することを確認するためにはるかに重要です
idmitriev

@idmitrievアプリケーションでこれを測定したところ、自動構成クラスを除外せずに73秒だったのに対し、アプリケーションは53秒で起動しました。上記のリストよりも多くのクラスを除外しました。
apkisbossin

すべての構成をインポートできます。BatchConfigurerConfiguration.JpaBatchConfigurationを処理するには、依存関係をプロジェクトに追加する必要がありますか?ConfigurationPropertiesRebinderAutoConfiguration#configurationPropertiesBeansなどの参照されたメソッドを処理する方法は?
user1767316

プライベート構成クラスを処理する方法は?
user1767316

44

これまでで最も投票された回答は間違いではありませんが、私が知りたい深さには及ばず、科学的な証拠もありません。Spring BootチームはBoot 2.0の起動時間を短縮するための演習を行い、チケット11226には多くの有用な情報が含まれています。条件評価にタイミング情報を追加するために開いているチケット7939もありますが、特定のETAがないようです。

起動時の起動をデバッグするための最も便利で体系的なアプローチは、Dave Syerによって行われました。https://github.com/dsyer/spring-boot-startup-bench

私も同様のユースケースを持っていたので、JMHを使用したDaveのマイクロベンチマーク手法を採用し、それを実行しました。結果は、ブートベンチマークプロジェクトです。GradleタスクbootJar(以前bootRepackageはBoot 1.5で呼び出されていました)によって生成された実行可能なjarを使用して、Spring Bootアプリケーションの起動時間を測定できるように設計しました。お気軽にご利用いただき、フィードバックをお寄せください。

私の調査結果は次のとおりです。

  1. CPUが重要です。たくさん。
  2. -Xverify:noneを使用してJVMを起動すると、非常に役立ちます。
  3. 不要な自動設定を除外すると役立ちます。
  4. DaveはJVM引数-XX:TieredStopAtLevel = 1を推奨しましたが、私のテストではそれによる大幅な改善は見られませんでした。また、-XX:TieredStopAtLevel=1おそらく最初のリクエストが遅くなります。
  5. ホスト名の解決が遅いという報告がありましたが、テストしたアプリに問題があるとは思いませんでした。

1
@ user991710破損の原因は不明ですが、修正されました。レポートをありがとう。
Abhijit Sarkar

2
これに追加して、カスタムアプリケーションで誰かがあなたのベンチマークをどのように使用するかについての例を追加していただけませんか?と同様のプロジェクトとして追加する必要がありますかminimal、それともjarファイルを単に提供するだけですか?前者を試してみましたが、それほど遠くはありませんでした。
user991710

1
-Xverify:noneコード検証に違反し、問題が発生する可能性があるため、本番環境で実行しないでください。-XX:TieredStopAtLevel=1アプリケーションを短時間(数秒)実行する場合は問題ありません。それ以外の場合は、JVMに長時間の最適化を提供するため、生産性が低下します。
loicmathieu

3
オラクルのドキュメントUse of -Xverify:none is unsupported.はそれが何を意味するのかリストしていますか?
さくら

1
多くのプール(確かにOracle UCPですが、私のテストではHikariとTomcatも)がプール内のデータを暗号化します。接続情報を暗号化しているのか、ストリームをラップしているのか、実際にはわかりません。とにかく、暗号化は乱数生成を使用するため、高可用性、高スループットのエントロピーソースを使用すると、パフォーマンスに顕著な違いが生じます。
ダニエル

18

Spring Boot 2.2.M1に は、Spring Bootでの遅延初期化をサポートする機能が追加されています。

デフォルトでは、アプリケーションコンテキストが更新されると、コンテキスト内のすべてのBeanが作成され、その依存関係が注入されます。対照的に、Bean定義が遅延して初期化されるように構成されている場合は、作成されず、必要になるまで依存関係は注入されません。

遅延初期化の有効化spring.main.lazy-initializationtrueに設定

遅延初期化を有効にするタイミング遅延初期化は、起動時間を大幅に改善することができますが、いくつかの顕著な欠点もあり、注意して有効にすることが重要です。

詳細については、ドキュメントを確認してください


3
遅延初期化を有効にすると、初回のロードは非常に高速になりますが、クライアントが初めてアクセスするときに、多少の遅延が発生する場合があります。これはプロダクションではなく開発に本当にお勧めします。
Isuru Dewasurendra

@IsuruDewasurendraが示唆したように、これは推奨される方法ではありません。アプリが負荷の処理を開始するときのレイテンシが大幅に増加する可能性があります。
Narendra Jaggi

それは道に缶を蹴るだけです。
Abhijit Sarkar

10

この質問/回答で説明されているように、最善の方法は、必要だと思うものだけを追加するのではなく、不要だとわかっている依存関係を除外することです。

参照:スプリングブートの起動時間を最小限に抑える

要約すれば:

コマンドラインからアプリケーションを起動するときに--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 {
}

4

ここで説明されている可能なアクションの完全なリストがあります:https : //spring.io/blog/2018/12/12/how-fast-is-spring

春側からの最も重要なメモを置きます(少し調整します):

  • Spring Boot Webスターターからのクラスパスの除外:
    • Hibernate Validator
    • ジャクソン(ただし、Spring Bootアクチュエータはそれに依存しています)JSONレンダリングが必要な場合は、Gsonを使用します(そのままのMVCでのみ機能します)。
    • ログバック:代わりにslf4j-jdk14を使用
  • spring-context-indexerを使用します。それは多くを追加するつもりはありませんが、少しでも役立ちます。
  • 余裕がない場合は、アクチュエータを使用しないでください。
  • Spring Boot 2.1およびSpring 5.1を使用します。利用可能になったら、2.2と5.2に切り替えます。
  • Spring Boot構成ファイルの場所をspring.config.location(コマンドライン引数またはシステムプロパティなど)で修正します。IDEでのテストの例:spring.config.location=file://./src/main/resources/application.properties
  • 必要ない場合はJMXをオフにしますspring.jmx.enabled=false(これはSpring Boot 2.2のデフォルトです)
  • デフォルトでBean定義を遅延させます。spring.main.lazy-initialization=trueSpring Boot 2.2には新しいフラグがあります(LazyInitBeanFactoryPostProcessor古いSpringで使用)。
  • ファットjarを解凍し、明示的なクラスパスで実行します。
  • でJVMを実行し-noverifyます。また、-XX:TieredStopAtLevel=1(起動時間の節約を犠牲にして、後でJITが遅くなります)についても検討してください。

上記LazyInitBeanFactoryPostProcessorspring.main.lazy-initialization=trueSpring 2.2から利用可能なフラグを適用できない場合は、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

それが役に立てば幸い!


0

私の場合、ブレークポイントが多すぎました。「ミュートブレークポイント」をクリックしてアプリケーションをデバッグモードで再起動すると、アプリケーションが10倍速く起動しました。


-1

手動テストのために開発のターンアラウンドを最適化しようとしている場合は、devtoolsの使用を強くお勧めします。

spring-boot-devtoolsを使用するアプリケーションは、クラスパス上のファイルが変更されるたびに自動的に再起動します。

再コンパイルするだけで、サーバーは自動的に再起動します(Groovyの場合は、ソースファイルを更新するだけで済みます)。IDE(「vscode」など)を使用している場合、Javaファイルが自動的にコンパイルされる可能性があるため、Javaファイルを保存するだけで間接的にサーバーの再起動を開始できます。この点で、JavaはGroovyと同じようにシームレスになります。

このアプローチの優れた点は、インクリメンタル再起動により、最初からの起動手順の一部が短絡されるため、サービスがより迅速にバックアップおよび実行されることです。


残念ながら、これは展開や自動ユニットテストの起動時間には役立ちません。


-1

警告: DBスキーマの自動生成にHibernate DDLを使用せず、L2キャッシュを使用しない場合、この回答は当てはまりません。先にスクロールします。

私の発見は、Hibernateがアプリケーションの起動にかなりの時間を追加することです。L2キャッシュとデータベースの初期化を無効にすると、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)

今、私はこのすべての自由時間で何をしますか?


hibernate.hbm2ddl.auto = updateはl2キャッシュとは関係ありません。ddl .. = updateは、現在のデータベーススキーマをスキャンし、エンティティを反映するようにスキーマを更新するために必要なSQLを計算することを指定します。「なし」はこの検証を実行しません(また、スキーマを更新しようとしません)。ベストプラクティスは、liquibaseのようなツールを使用することです。このツールでは、スキーマの変更を処理し、追跡することもできます。
Radu Toader

@RaduToaderこの質問と私の答えは、Spring Bootの起動時間を短縮することです。彼らはHibernate DDL対Liquibaseの議論とは何の関係もありません。これらのツールには、それぞれ長所と短所があります。私のポイントは、DBスキーマの更新を無効にして、必要な場合にのみ有効にできることです。(DBスキーマと自動生成されたスキーマを比較するために)最後の実行以降にモデルが変更されていない場合でも、Hibernateは起動時にかなりの時間がかかります。L2キャッシュについても同じことが言えます。
naXa

はい、私はそれを知っています、しかし私のポイントはこれがそれが本当に何をするか説明しないことは少し危険であるということでした。あなたは非常に簡単にあなたのデータベースを空にしてしまうかもしれません。
Radu Toader

@RaduToader私の回答には、DBの初期化に関するドキュメントページへのリンクがありました。あなたはそれを読みました?これには、最も人気のあるすべてのツール(HibernateとLiquibase、JPAとFlyway)をリストした網羅的なガイドが含まれています。また、今日は私の回答の上部に明確な警告を追加します。結果を説明するために他の変更が必要だと思いますか?
naXa

完璧です。ありがとう
Radu Toader

-3

以前にこれらの最適化を提案した人がいないのは不思議です。開発時のプロジェクトのビルドとスタートアップの最適化に関するいくつかの一般的なヒントを次に示します。

  • アンチウイルススキャナーから開発ディレクトリを除外します。
    • プロジェクトディレクトリ
    • ビルド出力ディレクトリ(プロジェクトディレクトリの外にある場合)
    • IDEインデックスディレクトリ(例:〜/ .IntelliJIdea2018.3)
    • デプロイメントディレクトリ(Tomcatのwebapps)
  • ハードウェアをアップグレードします。より速いCPUとRAM、より良いインターネット接続(依存関係のダウンロード用)、およびデータベース接続を使用し、SSDに切り替えます。ビデオカードは関係ありません。

警告

  1. 最初の選択肢は、セキュリティの低下という代償です。
  2. 2番目のオプションにはコストがかかります(明らかに)。

問題は、コンパイル時間ではなく、ブート時間の改善です。
ArtOfWarfare

@ArtOfWarfareがもう一度質問を読みました。質問は、問題を「開発フローを壊してしまうため、それだけ時間がかかるのは不幸だ」と述べています。これは主要な問題であると感じ、私の回答で対処しました。
naXa

-9

私には、間違った構成設定を使用しているように思えます。まず、myContainerと競合の可能性を確認します。誰が最も多くのリソースを使用しているかを判断するには、一度に各依存関係のメモリマップ(データの量を確認してください)を確認する必要があります。これには、かなりの時間がかかります(SUDO特権も)。ちなみに、通常は依存関係に対してコードをテストしていますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.