Mempercepat waktu startup Spring Boot

115

Saya memiliki aplikasi Spring Boot. Saya telah menambahkan banyak dependensi (sayangnya, sepertinya saya membutuhkan semuanya) dan waktu startup naik cukup banyak. Hanya melakukan SpringApplication.run(source, args)10 detik.

Meskipun itu mungkin tidak banyak dibandingkan dengan apa yang "biasa", saya tidak senang bahwa itu membutuhkan sebanyak itu, terutama karena itu merusak aliran pengembangan. Aplikasi itu sendiri agak kecil pada saat ini, jadi saya berasumsi sebagian besar waktu terkait dengan dependensi yang ditambahkan, bukan kelas aplikasi itu sendiri.

Saya berasumsi masalahnya adalah pemindaian classpath, tetapi saya tidak yakin bagaimana caranya:

  • Konfirmasikan bahwa masalahnya (yaitu cara "men-debug" Spring Boot)
  • Jika memang itu penyebabnya, bagaimana saya bisa membatasinya, jadi lebih cepat? Misalnya, jika saya mengetahui bahwa beberapa dependensi atau paket tidak berisi apa pun yang seharusnya dipindai Spring, apakah ada cara untuk membatasinya?

Saya berasumsi bahwa meningkatkan Spring agar memiliki inisialisasi kacang paralel selama startup akan mempercepat banyak hal, tetapi permintaan peningkatan itu telah dibuka sejak 2011, tanpa kemajuan apa pun. Saya melihat beberapa upaya lain di Spring Boot itu sendiri, seperti Investigate Tomcat JarScanning peningkatan kecepatan , tetapi itu khusus Tomcat dan telah ditinggalkan.

Artikel ini:

meskipun ditujukan untuk tes integrasi, menyarankan untuk menggunakan lazy-init=true, namun saya tidak tahu bagaimana menerapkan ini ke semua kacang di Spring Boot menggunakan konfigurasi Java - ada petunjuk di sini?

Setiap saran (lainnya) akan diterima.

hujan deras
sumber
Posting kode Anda. Biasanya hanya paket yang ditentukan oleh runner aplikasi yang dipindai. Jika Anda memiliki paket lain yang ditentukan untuk @ComponentScanitu dipindai juga. Hal lainnya adalah memastikan Anda belum mengaktifkan debug atau pelacakan logging karena umumnya logging lambat, sangat lambat.
M. Deinum
Jika Anda menggunakan Hibernate, ini juga cenderung memakan waktu yang lama saat aplikasi dimulai.
Knut Forkalsrud
Pengikatan otomatis musim semi berdasarkan jenis ditambah dengan biji pabrik berpotensi menjadi lambat jika Anda menambahkan banyak biji dan ketergantungan.
Knut Forkalsrud
Atau Anda dapat menggunakan caching, spring.io/guides/gs/caching
Cassian
2
Terima kasih atas semua komentarnya - Sayangnya saya tidak dapat memposting kode (banyak botol internal), namun saya masih mencari cara untuk men-debug ini. Ya, saya mungkin menggunakan A atau B atau melakukan X atau Y, yang memperlambatnya. Bagaimana cara menentukan ini? Jika saya menambahkan dependensi X, yang memiliki 15 dependensi transitif, bagaimana saya tahu mana dari 16 dependensi tersebut yang memperlambatnya? Jika saya bisa mencari tahu, apakah ada yang bisa saya lakukan nanti untuk menghentikan Spring dari memeriksa mereka? Petunjuk seperti itu akan berguna!
hujan stabil

Jawaban:

61

Spring Boot melakukan banyak konfigurasi otomatis yang mungkin tidak diperlukan. Jadi, Anda mungkin hanya ingin mempersempit konfigurasi otomatis yang diperlukan untuk aplikasi Anda. Untuk melihat daftar lengkap konfigurasi otomatis yang disertakan, jalankan pencatatan org.springframework.boot.autoconfiguredalam mode DEBUG ( logging.level.org.springframework.boot.autoconfigure=DEBUGdalam application.properties). Opsi lainnya adalah menjalankan aplikasi boot musim semi dengan --debugopsi:java -jar myproject-0.0.1-SNAPSHOT.jar --debug

Akan ada sesuatu seperti ini pada keluarannya:

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

Periksa daftar ini dan sertakan hanya konfigurasi otomatis yang Anda perlukan:

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

Kode telah disalin dari entri blog ini .

luboskrnac.dll
sumber
1
apakah kamu mengukur ini ??? Apakah itu lebih cepat ?? Menurut pendapat saya, ini adalah kasus yang luar biasa, jauh lebih penting untuk memastikan bahwa cache konteks pengujian Musim Semi berfungsi
idmitriev
@idmitriev Saya baru saja mengukur ini pada aplikasi saya dan aplikasi saya dimulai pada 53 detik, dibandingkan dengan tanpa mengeluarkan kelas konfigurasi otomatis adalah 73 detik. Saya mengecualikan lebih banyak kelas daripada yang tercantum di atas.
apkisbossin
Bagus untuk Mengimpor semua Konfigurasi. Bagaimana menangani BatchConfigurerConfiguration.JpaBatchConfiguration haruskah seseorang menambahkan ketergantungan ke projet? Bagaimana menangani metode yang dirujuk seperti ConfigurationPropertiesRebinderAutoConfiguration # configurationPropertiesBeans?
pengguna1767316
Bagaimana menangani kelas konfigurasi Private?
pengguna1767316
44

Jawaban yang paling banyak dipilih sejauh ini tidak salah, tapi tidak terlalu mendalam. Saya suka melihat dan tidak memberikan bukti ilmiah. Tim Boot Musim Semi melakukan latihan untuk mengurangi waktu startup untuk Boot 2.0, dan tiket 11226 berisi banyak informasi berguna. Ada juga tiket 7939 yang terbuka untuk menambahkan informasi waktu ke evaluasi kondisi, tetapi tampaknya tidak memiliki ETA khusus.

Pendekatan yang paling berguna dan metodis untuk debugging Boot startup telah dilakukan oleh Dave Syer. https://github.com/dsyer/spring-boot-startup-bench

Saya juga memiliki kasus penggunaan yang serupa, jadi saya mengambil pendekatan benchmark mikro Dave dengan JMH dan menjalankannya. Hasilnya adalah proyek boot-benchmark . Saya merancangnya sedemikian rupa sehingga dapat digunakan untuk mengukur waktu startup untuk aplikasi Spring Boot apa pun, menggunakan jar yang dapat dieksekusi yang dihasilkan oleh bootJar(sebelumnya disebut bootRepackagedalam Boot 1.5) tugas Gradle. Jangan ragu untuk menggunakannya dan memberikan umpan balik.

Temuan saya adalah sebagai berikut:

  1. CPU penting. Banyak.
  2. Memulai JVM dengan -Xverify: none membantu secara signifikan.
  3. Mengecualikan konfigurasi otomatis yang tidak perlu membantu.
  4. Dave merekomendasikan argumen JVM -XX: TieredStopAtLevel = 1 , tetapi pengujian saya tidak menunjukkan peningkatan yang signifikan dengan itu. Juga, -XX:TieredStopAtLevel=1mungkin akan memperlambat permintaan pertama Anda.
  5. Ada laporan tentang resolusi nama host yang lambat, tetapi saya tidak menganggapnya sebagai masalah untuk aplikasi yang saya uji.
Abhijit Sarkar
sumber
1
@ user991710 Tidak yakin bagaimana itu rusak, tapi sekarang sudah diperbaiki. Terima kasih atas laporannya.
Abhijit Sarkar
2
Untuk menambahkan ini, dapatkah Anda menambahkan contoh bagaimana seseorang dapat menggunakan benchmark Anda dengan aplikasi kustom? Apakah itu harus ditambahkan sebagai proyek yang mirip dengan minimal, atau mungkinkah toples disediakan begitu saja? Saya mencoba melakukan yang pertama tetapi tidak berhasil terlalu jauh.
pengguna991710
1
Jangan menjalankan -Xverify:noneproduksi karena merusak verifikasi kode dan Anda dapat mengalami masalah. -XX:TieredStopAtLevel=1Tidak apa-apa jika Anda menjalankan aplikasi dengan durasi kecil (beberapa detik) jika tidak maka akan kurang produktif karena akan memberikan pengoptimalan yang berjalan lama kepada JVM.
loicmathieu
3
dok oracle mendaftar Use of -Xverify:none is unsupported.apa artinya?
sakura
1
Banyak pool (Oracle UCP pasti, tetapi dalam pengujian saya juga Hikari dan Tomcat) mengenkripsi data di pool. Saya sebenarnya tidak tahu apakah mereka mengenkripsi info koneksi, atau membungkus streaming. Terlepas dari itu, enkripsi menggunakan pembuatan nomor acak sehingga memiliki sumber entropi throughput yang sangat tersedia dan tinggi membuat perbedaan kinerja yang nyata.
Daniel
19

Spring Boot 2.2.M1 telah menambahkan fitur untuk mendukung Inisialisasi Malas di Spring Boot.

Secara default, ketika konteks aplikasi di-refresh, setiap kacang dalam konteks dibuat dan dependensinya disuntikkan. Sebaliknya, ketika definisi kacang dikonfigurasikan untuk diinisialisasi dengan malas, ia tidak akan dibuat dan dependensinya tidak akan disuntikkan sampai dibutuhkan.

Mengaktifkan Inisialisasi Malas Setel spring.main.lazy-initializationke true

Kapan Mengaktifkan Inisialisasi Malas

inisialisasi yang lambat dapat menawarkan peningkatan yang signifikan dalam waktu mulai, tetapi ada beberapa kelemahan penting juga dan penting untuk mengaktifkannya dengan hati-hati

Untuk lebih jelasnya silahkan cek Doc

Niraj Sonawane
sumber
3
jika Anda mengaktifkan inisialisasi lambat, pemuatan pertama kali akan sangat cepat, tetapi saat klien mengakses untuk pertama kali, mungkin ada beberapa penundaan. Saya sangat merekomendasikan ini untuk pengembangan bukan untuk produksi.
Isuru Dewasurendra
Seperti yang disarankan @IsuruDewasurendra, ini bukan cara yang disarankan, ini dapat meningkatkan latensi secara signifikan saat aplikasi mulai menyajikan beban.
Narendra Jaggi
Itu hanya menendang kaleng di jalan.
Abhijit Sarkar
10

Seperti yang dijelaskan dalam pertanyaan / jawaban ini, menurut saya pendekatan terbaik adalah daripada menambahkan hanya yang Anda pikir Anda butuhkan, kecualikan dependensi yang Anda tahu tidak Anda butuhkan.

Lihat: Meminimalkan Waktu Startup Spring Boot

Singkatnya:

Anda dapat melihat apa yang terjadi di balik sampul dan mengaktifkan logging debug sesederhana menentukan --debug saat memulai aplikasi dari command-line. Anda juga bisa menentukan debug = true di application.properties Anda.

Selain itu, Anda dapat menyetel level logging di application.properties sesederhana:

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

Jika Anda mendeteksi modul yang dikonfigurasi otomatis yang tidak Anda inginkan, modul tersebut dapat dinonaktifkan. Dokumen untuk ini dapat ditemukan di sini: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration

Contohnya akan terlihat seperti:

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
pczeus.dll
sumber
4

Nah, ada seluruh daftar kemungkinan tindakan yang dijelaskan di sini: https://spring.io/blog/2018/12/12/how-fast-is-spring

Saya akan meletakkan catatan paling penting dari sisi Spring (disesuaikan sedikit):

  • Pengecualian classpath dari permulaan web Spring Boot:
    • Hibernate Validator
    • Jackson (tapi aktuator Spring Boot bergantung padanya). Gunakan Gson jika Anda memerlukan rendering JSON (hanya berfungsi dengan MVC di luar kotak).
    • Logback: gunakan slf4j-jdk14 sebagai gantinya
  • Gunakan pengindeks-konteks-pegas. Ini tidak akan menambah banyak, tetapi setiap sedikit membantu.
  • Jangan gunakan aktuator jika Anda mampu untuk tidak melakukannya.
  • Gunakan Spring Boot 2.1 dan Spring 5.1. Beralih ke 2.2 dan 5.2 jika tersedia.
  • Perbaiki lokasi file konfigurasi Spring Boot dengan spring.config.location(argumen baris perintah atau properti Sistem, dll.). Misalnya untuk pengujian di IDE: spring.config.location=file://./src/main/resources/application.properties.
  • Matikan JMX jika Anda tidak membutuhkannya spring.jmx.enabled=false(ini adalah default di Spring Boot 2.2)
  • Jadikan definisi kacang malas secara default. Ada bendera baru spring.main.lazy-initialization=truedi Spring Boot 2.2 (digunakan LazyInitBeanFactoryPostProcessoruntuk Spring yang lebih lama).
  • Buka kemasan tabung lemak dan jalankan dengan jalur kelas eksplisit.
  • Jalankan JVM dengan -noverify. Juga pertimbangkan -XX:TieredStopAtLevel=1(yang akan memperlambat JIT nanti dengan mengorbankan waktu startup yang dihemat).

Yang disebutkan LazyInitBeanFactoryPostProcessor(Anda dapat menggunakannya untuk Spring 1.5 jika Anda tidak dapat menggunakan flag yang spring.main.lazy-initialization=truetersedia dari Spring 2.2):

public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

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

Anda juga dapat menggunakan (atau menulis sendiri - sederhana) sesuatu untuk menganalisis waktu inisialisasi kacang: https://github.com/lwaddicor/spring-startup-analysis

Semoga membantu!

Przemek Nowak
sumber
0

Dalam kasus saya, ada terlalu banyak breakpoint. Ketika saya mengklik "Mute Breakpoints" dan memulai ulang aplikasi dalam mode debug, aplikasi dimulai dalam 10 kali lebih cepat.

Daulet Kadirbekov
sumber
-1

Jika Anda mencoba untuk mengoptimalkan pengembangan turn-around untuk pengujian manual, saya sangat merekomendasikan penggunaan devtools .

Aplikasi yang menggunakan spring-boot-devtools akan otomatis dimulai ulang setiap kali file di classpath berubah.

Kompilasi ulang saja - dan server akan restart sendiri (untuk Groovy Anda hanya perlu memperbarui file sumber). jika Anda menggunakan IDE (mis. 'vscode'), itu dapat secara otomatis mengkompilasi file java Anda, jadi hanya menyimpan file java dapat memulai restart server, secara tidak langsung - dan Java menjadi mulus seperti Groovy dalam hal ini.

Keindahan dari pendekatan ini adalah bahwa restart bertahap akan memutus beberapa langkah startup dari awal - sehingga layanan Anda akan kembali aktif dan berjalan lebih cepat!


Sayangnya, ini tidak membantu dengan waktu startup untuk penerapan atau pengujian unit otomatis.

Brent Bradburn
sumber
-1

PERINGATAN: Jika Anda tidak menggunakan Hibernate DDL untuk pembuatan skema DB otomatis dan Anda tidak menggunakan cache L2, jawaban ini TIDAK berlaku untuk Anda. Gulir ke depan.

Temuan saya adalah bahwa Hibernate menambahkan waktu yang signifikan untuk memulai aplikasi. Menonaktifkan cache L2 dan inisialisasi database menghasilkan startup aplikasi Spring Boot yang lebih cepat. Biarkan cache AKTIF untuk produksi dan nonaktifkan untuk lingkungan pengembangan Anda.

application.yml:

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

Hasil tes:

  1. L2 cache AKTIF dan 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 OFF dan 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)

Sekarang saya bertanya-tanya apa yang akan saya lakukan dengan semua waktu luang ini

naXa
sumber
hibernate.hbm2ddl.auto = pembaruan tidak ada hubungannya dengan cache l2. ddl .. = update menentukan untuk memindai skema database saat ini, dan menghitung sql yang diperlukan untuk memperbarui skema untuk mencerminkan entitas Anda. 'Tidak ada' tidak melakukan verifikasi ini (juga, tidak mencoba memperbarui skema). Praktik terbaiknya adalah menggunakan alat seperti liquibase, tempat Anda akan menangani perubahan skema, dan Anda juga dapat melacaknya.
Radu Toader
@RaduToader pertanyaan ini dan jawaban saya adalah tentang mempercepat waktu startup Spring Boot. Mereka tidak ada hubungannya dengan diskusi Hibernate DDL vs Liquibase; alat-alat ini memiliki pro dan kontra. Maksud saya adalah kita dapat menonaktifkan pembaruan skema DB dan mengaktifkannya hanya jika diperlukan. Hibernasi membutuhkan waktu yang signifikan saat permulaan bahkan ketika model tidak berubah sejak dijalankan terakhir (untuk membandingkan skema DB dengan skema yang dibuat secara otomatis). Hal yang sama juga berlaku untuk cache L2.
naXa
ya, saya tahu itu, tetapi maksud saya adalah bahwa ini agak berbahaya untuk tidak menjelaskan apa yang sebenarnya dilakukannya. Anda mungkin sangat mudah berakhir dengan db Anda kosong.
Radu Toader
@RaduToader Ada link ke halaman dokumentasi tentang inisialisasi DB di jawaban saya. Apakah kamu membacanya? Ini berisi panduan lengkap, mencantumkan semua alat paling populer (Hibernate dan Liquibase, serta JPA dan Flyway). Juga hari ini saya menambahkan peringatan yang jelas di bagian atas jawaban saya. Apakah menurut Anda saya memerlukan perubahan lain untuk menjelaskan konsekuensi?
naXa
Sempurna. Terima kasih
Radu Toader
-3

Saya merasa aneh tidak ada yang menyarankan pengoptimalan ini sebelumnya. Berikut adalah beberapa tip umum tentang mengoptimalkan pembuatan dan permulaan proyek saat mengembangkan:

  • kecualikan direktori pengembangan dari pemindai antivirus:
    • direktori proyek
    • membangun direktori keluaran (jika di luar direktori proyek)
    • Direktori indeks IDE (misalnya ~ / .IntelliJIdea2018.3)
    • direktori penyebaran (aplikasi web di Tomcat)
  • meningkatkan perangkat keras. gunakan CPU dan RAM yang lebih cepat, koneksi internet yang lebih baik (untuk mengunduh dependensi) dan koneksi database, alihkan ke SSD. kartu video tidak masalah.

PERINGATAN

  1. pilihan pertama datang dengan harga keamanan yang dikurangi.
  2. opsi kedua membutuhkan uang (jelas).
naXa
sumber
Pertanyaannya adalah tentang meningkatkan waktu boot, bukan waktu kompilasi.
ArtOfWarfare
@ArtOfWarfare membaca pertanyaan itu lagi. pertanyaannya menyatakan masalah sebagai "Saya tidak senang bahwa ini membutuhkan banyak [waktu], terutama karena hal itu merusak aliran pengembangan". Saya merasa ini adalah masalah utama dan mengatasinya dalam jawaban saya.
naXa
-9

Bagi saya, sepertinya Anda menggunakan pengaturan konfigurasi yang salah. Mulailah dengan memeriksa myContainer dan kemungkinan konflik. Untuk menentukan siapa yang menggunakan sumber daya paling banyak, Anda harus memeriksa peta memori (lihat jumlah data!) Untuk setiap ketergantungan pada satu waktu - dan itu membutuhkan banyak waktu, juga ... (dan hak istimewa SUDO). Ngomong-ngomong: apakah Anda biasanya menguji kode terhadap dependensi?


sumber