Ускорение запуска Spring Boot


115

У меня есть приложение Spring Boot. Я добавил много зависимостей (к сожалению, похоже, все они мне нужны), и время запуска значительно увеличилось. Просто выполнение SpringApplication.run(source, args)занимает 10 секунд.

Хотя это может быть немного по сравнению с тем, к чему «привыкли», я недоволен тем, что это занимает так много времени, в основном потому, что это нарушает процесс разработки. Само приложение на данный момент довольно мало, поэтому я предполагаю, что большую часть времени он связан с добавленными зависимостями, а не с самими классами приложения.

Я предполагаю, что проблема заключается в сканировании пути к классам, но я не знаю, как:

  • Подтвердите, что это проблема (например, как «отладить» Spring Boot)
  • Если это действительно причина, как я могу ее ограничить, чтобы она стала быстрее? Например, если я знаю, что какая-то зависимость или пакет не содержат ничего, что Spring должен сканировать, есть ли способ ограничить это?

Я предполагаю, что расширение Spring для параллельной инициализации bean-компонентов во время запуска ускорит процесс, но этот запрос на улучшение был открыт с 2011 года, без какого-либо прогресса. Я вижу некоторые другие усилия в самой Spring Boot, такие как Исследование улучшений скорости Tomcat JarScanning , но это специфично для Tomcat, и от него отказались.

Эта статья:

хотя и нацелен на интеграционные тесты, предлагает использовать lazy-init=true, однако я не знаю, как применить это ко всем bean-компонентам в Spring Boot с использованием конфигурации Java - здесь есть указатели?

Любые (другие) предложения приветствуются.


Разместите свой код. Обычно сканируется только тот пакет, который определен для запуска приложения. Если у вас есть другие пакеты, определенные для@ComponentScan них, также будут сканироваться. Другое дело - убедиться, что вы не включили отладку или ведение журнала трассировки, поскольку обычно ведение журнала происходит медленно, очень медленно.
M. Deinum

Если вы используете Hibernate, он также имеет тенденцию занимать значительное время при запуске приложения.
Knut Forkalsrud

Автоматическая привязка Spring по типу в сочетании с фабричными bean-компонентами может быть медленной, если вы добавляете много beans и зависимостей.
Knut Forkalsrud

Или вы можете использовать кеширование, spring.io/guides/gs/caching
Кассиан,

2
Спасибо всем за комментарии - к сожалению, я не смог бы опубликовать код (много внутренних банок), однако я все еще ищу способ отладить это. Да, я мог бы использовать A или B или X или Y, что замедляет его. Как это определить? Если я добавлю зависимость X, которая имеет 15 транзитивных зависимостей, как мне узнать, какая из этих 16 замедлила ее? Если я смогу узнать, могу ли я что-нибудь сделать позже, чтобы Spring не исследовал их? Такие указатели были бы полезны!
ровный дождь

Ответы:


62

Spring Boot выполняет множество автоматических настроек, которые могут не понадобиться. Поэтому вы можете сузить круг автоконфигурации, которая необходима вашему приложению. Чтобы увидеть полный список включенных автоконфигураций, просто запустите логирование org.springframework.boot.autoconfigureв режиме DEBUG ( logging.level.org.springframework.boot.autoconfigure=DEBUGin application.properties). Другой вариант - запустить приложение весенней загрузки с --debugопцией: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 {

Код был скопирован из этого сообщения в блоге .


1
ты это измерил ??? Это было намного быстрее ?? На мой взгляд, это исключительный случай, гораздо более важный, чтобы убедиться, что кеш контекста теста Spring работает
идмитриев 09

@idmitriev Я только что измерил это в своем приложении, и мое приложение запустилось за 53 секунды, тогда как без исключения классов автоконфигурации было 73 секунды. Однако я исключил гораздо больше классов, чем указано выше.
apkisbossin

Приятно импортировать всю конфигурацию. Как работать с BatchConfigurerConfiguration.JpaBatchConfiguration, если нужно добавить зависимость к проекту? Как работать с ссылочными методами, такими как ConfigurationPropertiesRebinderAutoConfiguration # configurationPropertiesBeans?
user1767316

Как работать с приватными классами конфигурации?
user1767316

44

Ответ, получивший наибольшее количество голосов до сих пор, не является неправильным, но он не входит в ту глубину, которую я хотел бы видеть, и не содержит научных доказательств. Команда Spring Boot выполнила упражнение по сокращению времени запуска для Boot 2.0, и билет 11226 содержит много полезной информации. Также есть билет 7939 открытый для добавления информации о времени для оценки состояния, но, похоже, он не имеет конкретного ETA.

Самый полезный и методичный подход к отладке запуска Boot принадлежит Дэйву Сайеру. https://github.com/dsyer/spring-boot-startup-bench

У меня тоже был аналогичный вариант использования, поэтому я взял подход Дейва к микротестированию с JMH и начал использовать его. Результатом является проект загрузочного тестирования . Я разработал его таким образом, чтобы его можно было использовать для измерения времени запуска любого приложения Spring Boot, используя исполняемый файл jar, созданный bootJar(ранее называвшийсяbootRepackage в Boot 1.5) задачей Gradle. Не стесняйтесь использовать его и оставлять отзывы.

Мои выводы таковы:

  1. ЦП имеет значение. Много.
  2. Запуск JVM с -Xverify: none существенно помогает.
  3. Помогает исключение ненужных автоконфигураций.
  4. Дэйв рекомендовал аргумент JVM -XX: TieredStopAtLevel = 1 , но мои тесты не показали значительного улучшения с этим. Кроме того, -XX:TieredStopAtLevel=1вероятно, замедлит ваш первый запрос.
  5. Были сообщения о медленном разрешении имени хоста, но я не обнаружил, что это проблема для тестируемых приложений.

1
@ user991710 Не знаю, как это сломалось, но теперь исправлено. Спасибо за отчет.
Abhijit Sarkar

2
Чтобы добавить к этому, не могли бы вы добавить пример того, как кто-то может использовать ваш тест с пользовательским приложением? Должен ли он быть добавлен как подобный проект minimal, или баночка может быть просто поставлена? Я попытался сделать первое, но не продвинулся далеко.
user991710

1
Не запускайте -Xverify:noneв производственной среде, поскольку это нарушает проверку кода, и вы можете столкнуться с проблемами. -XX:TieredStopAtLevel=1в порядке, если вы запускаете приложение в течение небольшого времени (несколько секунд), в противном случае оно будет менее продуктивным, так как обеспечит JVM длительной оптимизацией.
loicmathieu

3
списки оракул Дока , Use of -Xverify:none is unsupported.что это значит?
sakura

1
Многие пулы (обязательно Oracle UCP, но в моем тестировании также Hikari и Tomcat) шифруют данные в пуле. На самом деле я не знаю, шифруют ли они информацию о соединении или упаковывают поток. Тем не менее, шифрование использует генерацию случайных чисел, и поэтому наличие высокодоступного источника энтропии с высокой пропускной способностью существенно влияет на производительность.
Даниэль

19

В Spring Boot 2.2.M1 добавлена ​​функция поддержки отложенной инициализации в Spring Boot.

По умолчанию при обновлении контекста приложения создается каждый компонент в контексте и внедряются его зависимости. Напротив, когда определение bean-компонента настроено на ленивую инициализацию, оно не будет создано, и его зависимости не будут внедряться до тех пор, пока они не понадобятся.

Включение ленивой инициализации Установите spring.main.lazy-initializationзначение true

Когда включать ленивую инициализацию

ленивая инициализация может значительно сократить время запуска, но есть и некоторые заметные недостатки, и важно включить ее с осторожностью

Для получения дополнительной информации, пожалуйста, проверьте документ


3
если вы включите ленивую инициализацию, первая загрузка будет очень быстрой, но при первом обращении клиента может возникнуть некоторая задержка. Я действительно рекомендую это для разработки, а не для производства.
Исуру Девасурендра,

Как предположил @IsuruDewasurendra, это совершенно не рекомендуемый способ, он может значительно увеличить задержку, когда приложение начинает обслуживать нагрузку.
Нарендра Джагги

Он просто выбивает из колеи.
Абхиджит Саркар,

10

Как описано в этом вопросе / ответе, я думаю, что лучший подход - вместо добавления только тех, которые, по вашему мнению, вам нужны, исключить зависимости, которые, как вы знаете, вам не нужны.

См .: Минимизация времени запуска Spring Boot

В итоге:

Вы можете увидеть, что происходит под обложками, и включить ведение журнала отладки, просто указав --debug при запуске приложения из командной строки. Вы также можете указать debug = true в своем application.properties.

Кроме того, вы можете установить уровень ведения журнала в 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-disables-specific-auto-configuration

Пример мог бы выглядеть так:

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

4

Вот полный список возможных действий, описанных здесь: https://spring.io/blog/2018/12/12/how-fast-is-spring

Я поставлю самые важные замечания со стороны Spring (немного скорректировано):

  • Исключения пути к классам из веб-стартеров Spring Boot:
    • Валидатор гибернации
    • Джексон (но от этого зависят приводы Spring Boot). Используйте Gson, если вам нужен рендеринг JSON (работает только с MVC из коробки).
    • Возврат: вместо этого используйте slf4j-jdk14
  • Используйте spring-context-indexer. Это не добавит многого, но каждая мелочь помогает.
  • Не используйте приводы, если вы можете себе это позволить.
  • Используйте Spring Boot 2.1 и Spring 5.1. Переключитесь на 2.2 и 5.2, когда они доступны.
  • Исправьте расположение файлов конфигурации Spring Boot с помощью spring.config.location(аргумент командной строки или свойство System и т. Д.). Пример для тестирования в 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).
  • Распакуйте толстую банку и запустите с явным путем к классам.
  • Запустите JVM с помощью -noverify. Также учтите -XX:TieredStopAtLevel=1(это замедлит JIT позже за счет сэкономленного времени запуска).

Упомянутое LazyInitBeanFactoryPostProcessor(вы можете использовать его для Spring 1.5, если вы не можете применить флаг, spring.main.lazy-initialization=trueдоступный из 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);
      }
  }
}

Вы также можете использовать (или написать свое собственное - это просто) что-нибудь для анализа времени инициализации beans: https://github.com/lwaddicor/spring-startup-analysis

Надеюсь, поможет!


0

В моем случае было слишком много точек останова. Когда я нажал кнопку «Mute Breakpoints» и перезапустил приложение в режиме отладки, приложение запустилось в 10 раз быстрее.


-1

Если вы пытаетесь оптимизировать цикл разработки для ручного тестирования, я настоятельно рекомендую использовать инструменты разработчика .

Приложения, использующие spring-boot-devtools, будут автоматически перезапускаться при изменении файлов в пути к классам.

Просто перекомпилируйте - и сервер перезапустится (для Groovy вам нужно только обновить исходный файл). если вы используете IDE (например, vscode), она может автоматически компилировать ваши java-файлы, поэтому простое сохранение java-файла может косвенно инициировать перезапуск сервера - и в этом отношении Java становится такой же простой, как Groovy.

Прелесть этого подхода в том, что инкрементный перезапуск сокращает некоторые этапы запуска с нуля, поэтому ваша служба будет восстановлена ​​и запущена намного быстрее!


К сожалению, это не помогает сократить время запуска для развертывания или автоматического модульного тестирования.


-1

ПРЕДУПРЕЖДЕНИЕ: Если вы не используете 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

@RaduToader этот вопрос и мой ответ касаются ускорения времени запуска Spring Boot. Они не имеют ничего общего с обсуждением Hibernate DDL и Liquibase; У этих инструментов есть свои плюсы и минусы. Я хочу сказать, что мы можем отключить обновление схемы БД и включить его только при необходимости. Hibernate занимает значительное время при запуске, даже если модель не изменилась с момента последнего запуска (для сравнения схемы БД с автоматически созданной схемой). То же самое и с кешем второго уровня.
naXa

да, я знаю это, но я хотел сказать, что немного опасно не объяснять, что он на самом деле делает. Вы можете очень легко получить пустую базу данных.
Radu

@RaduToader В моем ответе была ссылка на страницу документации об инициализации БД. Вы это читали? Он содержит исчерпывающее руководство, в котором перечислены все самые популярные инструменты (Hibernate и Liquibase, а также JPA и Flyway). Также сегодня я добавляю четкое предупреждение в начало своего ответа. Как вы думаете, мне нужны другие изменения, чтобы объяснить последствия?
naXa

Отлично. Спасибо
Radu

-3

Мне кажется странным, что раньше никто не предлагал эту оптимизацию. Вот несколько общих советов по оптимизации сборки проекта и запуска при разработке:

  • исключить каталоги разработки из антивирусного сканера:
    • каталог проекта
    • выходной каталог сборки (если он находится вне каталога проекта)
    • Каталог индексов IDE (например, ~ / .IntelliJIdea2018.3)
    • каталог развертывания (веб-приложения в Tomcat)
  • обновить оборудование. используйте более быстрый процессор и оперативную память, лучшее подключение к Интернету (для загрузки зависимостей) и подключение к базе данных, переключитесь на SSD. видеокарта не имеет значения.

ПРЕДУПРЕЖДЕНИЯ

  1. первый вариант обходится ценой снижения безопасности.
  2. второй вариант стоит денег (очевидно).

Вопрос в том, чтобы улучшить время загрузки, а не время компиляции.
ArtOfWarfare

@ArtOfWarfare прочтите вопрос еще раз. в вопросе говорится о проблеме: «Я недоволен тем, что на это уходит так много [времени], в основном потому, что это мешает процессу разработки». Я чувствовал, что это основная проблема, и решил ее в своем ответе.
naXa

-9

Мне кажется, что вы используете неправильную настройку конфигурации. Начните с проверки myContainer и возможных конфликтов. Чтобы определить, кто использует больше всего ресурсов, вам нужно проверить карты памяти (увидеть объем данных!) Для каждой зависимости за раз - а это также занимает много времени ... (и привилегии SUDO). Кстати: вы обычно тестируете код на зависимости?

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.