Beschleunigen Sie die Startzeit von Spring Boot


113

Ich habe eine Spring Boot-Anwendung. Ich habe viele Abhängigkeiten hinzugefügt (leider brauche ich alle) und die Startzeit ist ziemlich hoch. Nur ein SpringApplication.run(source, args)dauert 10 Sekunden.

Das ist zwar nicht viel im Vergleich zu dem, was "gewohnt" ist, aber ich bin unglücklich, dass es so viel kostet, vor allem, weil es den Entwicklungsfluss unterbricht. Die Anwendung selbst ist zu diesem Zeitpunkt eher klein, daher gehe ich davon aus, dass die meiste Zeit mit den hinzugefügten Abhängigkeiten zusammenhängt, nicht mit den App-Klassen selbst.

Ich gehe davon aus, dass das Problem das Scannen von Klassenpfaden ist, bin mir aber nicht sicher, wie:

  • Bestätigen Sie, dass dies das Problem ist (dh wie Sie Spring Boot "debuggen").
  • Wenn es wirklich die Ursache ist, wie kann ich es begrenzen, damit es schneller wird? Wenn ich zum Beispiel weiß, dass eine Abhängigkeit oder ein Paket nichts enthält, was Spring scannen sollte, gibt es eine Möglichkeit, dies einzuschränken?

Ich gehe davon aus, dass die Erweiterung von Spring auf eine parallele Bean-Initialisierung während des Startvorgangs die Dinge beschleunigen würde, aber diese Erweiterungsanforderung ist seit 2011 ohne Fortschritte geöffnet. Ich sehe einige andere Bemühungen in Spring Boot selbst, wie z. B. die Verbesserung der Geschwindigkeit von Tomcat JarScanning untersuchen , aber das ist Tomcat-spezifisch und wurde aufgegeben.

Dieser Beitrag:

Obwohl auf Integrationstests ausgerichtet, schlägt die Verwendung vor lazy-init=true , aber ich weiß nicht, wie ich dies auf alle Beans in Spring Boot unter Verwendung der Java-Konfiguration anwenden soll - irgendwelche Hinweise hier?

Alle (anderen) Vorschläge wären willkommen.


Veröffentlichen Sie Ihren Code. Normalerweise wird nur das Paket gescannt, für das der Anwendungsläufer definiert ist. Wenn Sie andere Pakete definiert haben @ComponentScan, werden diese ebenfalls gescannt. Eine andere Sache ist, sicherzustellen, dass Sie die Debug- oder Trace-Protokollierung nicht aktiviert haben, da die Protokollierung im Allgemeinen langsam, sehr langsam ist.
M. Deinum

Wenn Sie den Ruhezustand verwenden, wird beim Start der Anwendung häufig viel Zeit benötigt.
Knut Forkalsrud

Die automatische Bindung von Spring nach Typ in Verbindung mit Factory-Beans kann langsam sein, wenn Sie viele Beans und Abhängigkeiten hinzufügen.
Knut Forkalsrud

Oder Sie können Caching verwenden, spring.io/guides/gs/caching
Cassian

2
Vielen Dank für die Kommentare - ich könnte den Code leider nicht veröffentlichen (viele interne Gläser), aber ich suche immer noch nach einer Möglichkeit, dies zu debuggen. Ja, ich benutze möglicherweise A oder B oder mache X oder Y, was es verlangsamt. Wie bestimme ich das? Wenn ich eine Abhängigkeit X hinzufüge, die 15 transitive Abhängigkeiten aufweist, woher weiß ich, welche dieser 16 sie verlangsamt hat? Wenn ich es herausfinden kann, kann ich später etwas tun, um Spring davon abzuhalten, sie zu untersuchen? Solche Zeiger wären nützlich!
stetiger Regen

Antworten:


61

Spring Boot führt viele automatische Konfigurationen durch, die möglicherweise nicht benötigt werden. Daher möchten Sie möglicherweise nur die für Ihre App erforderliche automatische Konfiguration eingrenzen. Um die vollständige Liste der enthaltenen automatischen Konfigurationen anzuzeigen, führen Sie einfach die Protokollierung org.springframework.boot.autoconfigureim DEBUG-Modus ( logging.level.org.springframework.boot.autoconfigure=DEBUGin application.properties) aus. Eine weitere Option ist das Ausführen der Spring Boot-Anwendung mit der folgenden --debugOption:java -jar myproject-0.0.1-SNAPSHOT.jar --debug

Es würde so etwas in der Ausgabe geben:

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

Überprüfen Sie diese Liste und geben Sie nur die erforderlichen Autokonfigurationen an:

@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 {

Code wurde aus diesem Blog-Beitrag kopiert .


1
hast du das gemessen ??? War es viel schneller? Meiner Meinung nach ist dies ein Ausnahmefall, viel wichtiger, um sicherzustellen, dass der Spring-Test-Kontext-Cache funktioniert
idmitriev

@idmitriev Ich habe dies gerade in meiner Anwendung gemessen und meine Anwendung wurde nach 53 Sekunden gestartet, verglichen mit 73 Sekunden ohne Ausschluss der Autokonfigurationsklassen. Ich habe jedoch viel mehr Klassen als oben aufgeführt ausgeschlossen.
Apkisbossin

Schön, alle Konfigurationen zu importieren. Wie gehe ich mit BatchConfigurerConfiguration.JpaBatchConfiguration um? Sollte man die Abhängigkeit zum Projekt hinzufügen? Wie gehe ich mit referenzierten Methoden wie ConfigurationPropertiesRebinderAutoConfiguration # configurationPropertiesBeans um?
user1767316

Wie gehe ich mit privaten Konfigurationsklassen um?
user1767316

44

Die bisher am häufigsten gewählte Antwort ist nicht falsch, geht aber nicht in die Tiefe, die ich gerne sehe, und liefert keine wissenschaftlichen Beweise. Das Spring Boot-Team hat eine Übung zur Verkürzung der Startzeit für Boot 2.0 durchgeführt, und Ticket 11226 enthält viele nützliche Informationen. Es gibt auch ein Ticket 7939, das zum Hinzufügen von Zeitinformationen zur Zustandsbewertung geöffnet ist, aber es scheint keine spezifische ETA zu haben.

Der nützlichste und methodischste Ansatz zum Debuggen des Boot-Starts wurde von Dave Syer durchgeführt. https://github.com/dsyer/spring-boot-startup-bench

Ich hatte auch einen ähnlichen Anwendungsfall, also nahm ich Daves Ansatz des Mikro-Benchmarking mit JMH und lief damit. Das Ergebnis ist das Boot-Benchmark- Projekt. Ich habe es so konzipiert, dass es zum Messen der Startzeit für jede Spring Boot-Anwendung verwendet werden kann, indem das ausführbare JAR verwendet wird, das von der Gradle-Task bootJar(zuvor bootRepackagein Boot 1.5 genannt) erstellt wurde. Fühlen Sie sich frei, es zu verwenden und Feedback zu geben.

Meine Ergebnisse sind wie folgt:

  1. CPU ist wichtig. Viel.
  2. Starten der JVM mit -Xverify: Keine hilft wesentlich.
  3. Das Ausschließen unnötiger Autokonfigurationen hilft.
  4. Dave empfahl das JVM-Argument -XX: TieredStopAtLevel = 1 , aber meine Tests zeigten keine signifikante Verbesserung. Auch -XX:TieredStopAtLevel=1würde wahrscheinlich Ihre erste Anfrage verlangsamen.
  5. Es wurde berichtet, dass die Auflösung von Hostnamen langsam ist, aber ich fand es nicht problematisch für die von mir getesteten Apps.

1
@ user991710 Ich bin mir nicht sicher, wie es kaputt gegangen ist, aber es ist jetzt behoben. Danke für den Bericht.
Abhijit Sarkar

2
Könnten Sie bitte ein Beispiel hinzufügen, wie jemand Ihren Benchmark mit einer benutzerdefinierten Anwendung verwenden könnte? Muss es als ähnliches Projekt hinzugefügt werden minimaloder kann das Glas einfach geliefert werden? Ich habe versucht, das erstere zu tun, bin aber nicht weit gekommen.
user991710

1
Laufen Sie nicht -Xverify:nonein der Produktion, da dies die Codeüberprüfung unterbricht und Sie auf Probleme stoßen können. -XX:TieredStopAtLevel=1ist in Ordnung, wenn Sie eine Anwendung für eine kurze Dauer (einige Sekunden) ausführen, da sie sonst weniger produktiv ist, da die JVM über einen längeren Zeitraum optimiert wird.
Loicmathieu

3
Das Orakel-Dokument listet auf, Use of -Xverify:none is unsupported.was es bedeutet.
Sakura

1
Viele Pools (Oracle UCP sicher, aber in meinen Tests auch Hikari und Tomcat) verschlüsseln Daten im Pool. Ich weiß eigentlich nicht, ob sie die Verbindungsinformationen verschlüsseln oder den Stream umbrechen. Unabhängig davon wird bei der Verschlüsselung eine Zufallszahlengenerierung verwendet, sodass eine hochverfügbare Entropiequelle mit hohem Durchsatz einen spürbaren Leistungsunterschied darstellt.
Daniel

18

Spring Boot 2.2.M1 hat eine Funktion hinzugefügt, die die verzögerte Initialisierung in Spring Boot unterstützt.

Wenn ein Anwendungskontext aktualisiert wird, wird standardmäßig jede Bean im Kontext erstellt und ihre Abhängigkeiten eingefügt. Im Gegensatz dazu wird eine Bean-Definition, die so konfiguriert ist, dass sie träge initialisiert wird, nicht erstellt und ihre Abhängigkeiten werden erst eingefügt, wenn sie benötigt werden.

Aktivieren Verzögerte Initialisierung Set spring.main.lazy-initializationzu wahren

Wann die verzögerte Initialisierung aktiviert werden kann Die verzögerte Initialisierung kann die Startzeit erheblich verbessern, es gibt jedoch auch einige bemerkenswerte Nachteile, und es ist wichtig, sie sorgfältig zu aktivieren

Weitere Informationen finden Sie in Doc


3
Wenn Sie die verzögerte Initialisierung aktivieren, ist das erstmalige Laden sehr schnell, aber wenn der Client zum ersten Mal darauf zugreift, kann es zu Verzögerungen kommen. Ich empfehle dies wirklich für die Entwicklung, nicht für die Produktion.
Isuru Dewasurendra

Wie @IsuruDewasurendra vorgeschlagen hat, ist dies zu Recht keine empfohlene Methode. Sie kann die Latenz erheblich erhöhen, wenn die App die Last bedient.
Narendra Jaggi

Es tritt nur die Dose die Straße hinunter.
Abhijit Sarkar

10

Wie in dieser Frage / Antwort beschrieben, ist es meiner Meinung nach der beste Ansatz, anstatt nur diejenigen hinzuzufügen, die Sie für nötig halten, die Abhängigkeiten auszuschließen, von denen Sie wissen, dass Sie sie nicht benötigen.

Siehe: Startzeit des Spring Boot minimieren

Zusammenfassend:

Sie können sehen, was unter der Decke vor sich geht, und die Debug-Protokollierung aktivieren, indem Sie einfach --debug angeben, wenn Sie die Anwendung über die Befehlszeile starten. Sie können debug = true auch in Ihrer application.properties angeben.

Sie können die Protokollierungsstufe in application.properties auch so einfach festlegen wie:

logging.level.org.springframework.web: DEBUG logging.level.org.hibernate: ERROR

Wenn Sie ein automatisch konfiguriertes Modul erkennen, das Sie nicht möchten, kann es deaktiviert werden. Die Dokumente dazu finden Sie hier: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration

Ein Beispiel würde aussehen wie:

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

4

Nun, es gibt eine vollständige Liste möglicher Aktionen, die hier beschrieben werden: https://spring.io/blog/2018/12/12/how-fast-is-spring

Ich werde die wichtigsten Noten von der Frühlingsseite setzen (ein wenig angepasst):

  • Klassenpfadausschlüsse von Spring Boot-Webstartern:
    • Validator für den Ruhezustand
    • Jackson (aber Spring Boot-Aktuatoren hängen davon ab). Verwenden Sie Gson, wenn Sie JSON-Rendering benötigen (funktioniert nur mit MVC ab Werk).
    • Logback: Verwenden Sie stattdessen slf4j-jdk14
  • Verwenden Sie den Spring-Context-Indexer. Es wird nicht viel hinzufügen, aber jedes bisschen hilft.
  • Verwenden Sie keine Aktuatoren, wenn Sie es sich leisten können, dies nicht zu tun.
  • Verwenden Sie Spring Boot 2.1 und Spring 5.1. Wechseln Sie zu 2.2 und 5.2, wenn sie verfügbar sind.
  • Korrigieren Sie den Speicherort der Spring Boot-Konfigurationsdatei (en) mit spring.config.location(Befehlszeilenargument oder Systemeigenschaft usw.). Beispiel zum Testen in IDE : spring.config.location=file://./src/main/resources/application.properties.
  • Schalten Sie JMX aus, wenn Sie es nicht benötigen spring.jmx.enabled=false(dies ist die Standardeinstellung in Spring Boot 2.2).
  • Machen Sie Bean-Definitionen standardmäßig faul. spring.main.lazy-initialization=trueIn Spring Boot 2.2 gibt es eine neue Flagge ( LazyInitBeanFactoryPostProcessorfür ältere Spring verwenden).
  • Packen Sie das fette Glas aus und führen Sie es mit einem expliziten Klassenpfad aus.
  • Führen Sie die JVM mit aus -noverify. Berücksichtigen Sie auch -XX:TieredStopAtLevel=1(dies verlangsamt die JIT später auf Kosten der gespeicherten Startzeit).

Die genannten LazyInitBeanFactoryPostProcessor(Sie können es für Spring 1.5 verwenden, wenn Sie das spring.main.lazy-initialization=trueab Spring 2.2 verfügbare Flag nicht anwenden können ):

public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

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

Sie können auch etwas verwenden (oder Ihr eigenes schreiben - es ist einfach), um die Initialisierungszeit von Beans zu analysieren: https://github.com/lwaddicor/spring-startup-analysis

Ich hoffe es hilft!


0

In meinem Fall gab es zu viele Haltepunkte. Als ich auf "Haltepunkte stumm schalten" klickte und die Anwendung im Debug-Modus neu startete, wurde die Anwendung zehnmal schneller gestartet.


-1

Wenn Sie versuchen, die Entwicklungsabwicklung für manuelle Tests zu optimieren, empfehle ich dringend die Verwendung von devtools .

Anwendungen, die spring-boot-devtools verwenden, werden automatisch neu gestartet, wenn sich Dateien im Klassenpfad ändern.

Einfach neu kompilieren - und der Server startet sich selbst neu (für Groovy müssen Sie nur die Quelldatei aktualisieren). Wenn Sie eine IDE verwenden (z. B. 'vscode'), werden Ihre Java-Dateien möglicherweise automatisch kompiliert. Wenn Sie also nur eine Java-Datei speichern, kann indirekt ein Neustart des Servers eingeleitet werden - und Java wird in dieser Hinsicht genauso nahtlos wie Groovy.

Das Schöne an diesem Ansatz ist, dass der inkrementelle Neustart einige der Startschritte von Grund auf kurzschließt - so dass Ihr Dienst viel schneller wieder betriebsbereit ist!


Leider hilft dies nicht bei Startzeiten für die Bereitstellung oder automatisierten Komponententests.


-1

WARNUNG: Wenn Sie Hibernate DDL nicht für die automatische Generierung von DB-Schemas verwenden und keinen L2-Cache verwenden, gilt diese Antwort NICHT für Sie. Scrollen Sie weiter.

Mein Ergebnis ist, dass der Ruhezustand den Start der Anwendung erheblich verlängert. Das Deaktivieren der L2-Cache- und Datenbankinitialisierung führt zu einem schnelleren Start der Spring Boot-App. Lassen Sie den Cache für die Produktion eingeschaltet und deaktivieren Sie ihn für Ihre Entwicklungsumgebung.

application.yml:

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

Testergebnisse:

  1. L2-Cache ist eingeschaltet und 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 Cache ist AUS und 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)

Jetzt frage ich mich, was ich mit all dieser Freizeit anfangen soll


hibernate.hbm2ddl.auto = update hat nichts mit l2 cache zu tun. ddl .. = update gibt an, dass das aktuelle Datenbankschema gescannt und die erforderliche SQL berechnet werden soll, um das Schema so zu aktualisieren, dass es Ihre Entitäten widerspiegelt. 'Keine' führt diese Überprüfung nicht durch (versucht auch nicht, das Schema zu aktualisieren). Best Practices sind die Verwendung eines Tools wie Liquibase, mit dem Sie Ihre Schemaänderungen bearbeiten und auch verfolgen können.
Radu Toader

@RaduToader Bei dieser Frage und meiner Antwort geht es darum, die Startzeit von Spring Boot zu beschleunigen. Sie haben nichts mit der Diskussion zwischen Hibernate DDL und Liquibase zu tun. Diese Tools haben Vor- und Nachteile. Mein Punkt ist, dass wir das DB-Schema-Update deaktivieren und nur bei Bedarf aktivieren können. Der Ruhezustand benötigt beim Start viel Zeit, auch wenn sich das Modell seit dem letzten Lauf nicht geändert hat (zum Vergleichen des DB-Schemas mit dem automatisch generierten Schema). Der gleiche Punkt gilt für den L2-Cache.
NaXa

Ja, das weiß ich, aber mein Punkt war, dass es ein bisschen gefährlich ist, nicht zu erklären, was es wirklich tut. Sie könnten sehr leicht mit Ihrer Datenbank leer enden.
Radu Toader

@RaduToader In meiner Antwort war ein Link zu einer Dokumentationsseite über die DB-Initialisierung enthalten. Hast du es gelesen? Es enthält eine ausführliche Anleitung mit allen gängigen Tools (Hibernate und Liquibase sowie JPA und Flyway). Auch heute füge ich meiner Antwort eine klare Warnung hinzu. Glaubst du, ich brauche noch andere Änderungen, um die Konsequenzen zu erklären?
NaXa

Perfekt. Vielen Dank
Radu Toader

-3

Ich finde es seltsam, dass noch niemand diese Optimierungen vorgeschlagen hat. Hier sind einige allgemeine Tipps zur Optimierung der Projekterstellung und des Starts bei der Entwicklung:

  • Entwicklungsverzeichnisse vom Antivirenscanner ausschließen:
    • Projektverzeichnis
    • Ausgabeverzeichnis erstellen (wenn es außerhalb des Projektverzeichnisses liegt)
    • IDE-Indexverzeichnis (z. B. ~ / .IntelliJIdea2018.3)
    • Bereitstellungsverzeichnis (Webanwendungen in Tomcat)
  • Hardware aktualisieren. Verwenden Sie schnellere CPU und RAM, bessere Internetverbindung (zum Herunterladen von Abhängigkeiten) und Datenbankverbindung, wechseln Sie zu SSD. Eine Grafikkarte spielt keine Rolle.

WARNHINWEISE

  1. Die erste Option ist der Preis für reduzierte Sicherheit.
  2. Die zweite Option kostet (offensichtlich) Geld.

Die Frage betrifft die Verbesserung der Startzeit, nicht die Kompilierungszeit.
ArtOfWarfare

@ArtOfWarfare las die Frage noch einmal. In der Frage heißt es: "Ich bin unglücklich, dass es so viel Zeit in Anspruch nimmt, hauptsächlich, weil es den Entwicklungsfluss unterbricht." Ich hatte das Gefühl, dass dies ein Hauptproblem ist, und habe es in meiner Antwort angesprochen.
NaXa

-9

Für mich klingt es so, als würden Sie eine falsche Konfigurationseinstellung verwenden. Überprüfen Sie zunächst myContainer und mögliche Konflikte. Um festzustellen, wer die meisten Ressourcen verwendet, müssen Sie die Speicherzuordnungen (siehe Datenmenge!) Für jede Abhängigkeit gleichzeitig überprüfen - und das nimmt auch viel Zeit in Anspruch ... (und SUDO-Berechtigungen). Übrigens: Testen Sie den Code normalerweise anhand der Abhängigkeiten?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.