Tăng tốc thời gian khởi động Spring Boot


114

Tôi có một ứng dụng Spring Boot. Tôi đã thêm rất nhiều phụ thuộc (thật không may, có vẻ như tôi cần tất cả chúng) và thời gian khởi động tăng lên khá nhiều. Chỉ làm một SpringApplication.run(source, args)mất 10 giây.

Mặc dù điều đó có thể không nhiều so với những gì đã được "sử dụng", nhưng tôi không hài lòng vì nó mất nhiều như vậy, chủ yếu là vì nó phá vỡ quy trình phát triển. Bản thân ứng dụng tại thời điểm này khá nhỏ, vì vậy tôi cho rằng phần lớn thời gian liên quan đến các phần phụ thuộc được thêm vào, không phải bản thân các lớp ứng dụng.

Tôi cho rằng sự cố là do quét classpath, nhưng tôi không chắc chắn về cách:

  • Xác nhận đó là sự cố (tức là cách "gỡ lỗi" Spring Boot)
  • Nếu thực sự là nguyên nhân thì làm cách nào để hạn chế, để bệnh nhanh khỏi? Ví dụ: nếu tôi biết rằng một số gói hoặc gói phụ thuộc không chứa bất kỳ thứ gì mà Spring nên quét, có cách nào để hạn chế điều đó không?

Tôi giả định rằng việc nâng cao Spring để khởi tạo bean song song trong quá trình khởi động sẽ tăng tốc mọi thứ, nhưng yêu cầu nâng cao đó đã được mở từ năm 2011 mà không có bất kỳ tiến triển nào. Tôi thấy một số nỗ lực khác trong Spring Boot, chẳng hạn như Điều tra cải tiến tốc độ JarScanning của Tomcat , nhưng đó là Tomcat cụ thể và đã bị bỏ qua.

Bài viết này:

mặc dù nhằm mục đích kiểm tra tích hợp, đề xuất sử dụng lazy-init=true, tuy nhiên tôi không biết cách áp dụng điều này cho tất cả các bean trong Spring Boot bằng cách sử dụng cấu hình Java - bất kỳ con trỏ nào ở đây?

Mọi đề xuất (khác) sẽ được hoan nghênh.


Đăng mã của bạn. Thông thường, chỉ gói phần mềm chạy ứng dụng được xác định mới được quét. Nếu bạn có các gói khác được xác định cho @ComponentScannhững gói đó cũng được quét. Một điều nữa là đảm bảo rằng bạn chưa bật tính năng ghi nhật ký gỡ lỗi hoặc theo dõi vì ghi nhật ký nói chung rất chậm, rất chậm.
M. Deinum

Nếu bạn sử dụng Hibernate, nó cũng có xu hướng tiêu tốn thời gian đáng kể khi khởi động ứng dụng.
Knut Forkalsrud

Tự động liên kết theo loại của Spring kết hợp với các loại đậu xuất xưởng có khả năng bị chậm khi bạn thêm nhiều loại đậu và phụ thuộc.
Knut Forkalsrud

Hoặc bạn có thể sử dụng bộ nhớ đệm, spring.io/guides/gs/caching
Cassian

2
Cảm ơn tất cả các ý kiến ​​- Rất tiếc, tôi sẽ không thể đăng mã (rất nhiều lọ bên trong), tuy nhiên tôi vẫn đang tìm cách để gỡ lỗi này. Có, tôi có thể đang sử dụng A hoặc B hoặc làm X hoặc Y, điều này làm chậm lại. Làm cách nào để xác định điều này? Nếu tôi thêm một phụ thuộc X, có 15 phụ thuộc bắc cầu, làm cách nào để biết cái nào trong 16 phụ thuộc đó làm chậm nó? Nếu tôi có thể tìm ra, tôi có thể làm gì sau này để ngăn Spring kiểm tra chúng không? Những con trỏ như vậy sẽ hữu ích!
mưa ổn định vào

Câu trả lời:


61

Spring Boot thực hiện rất nhiều cấu hình tự động có thể không cần thiết. Vì vậy, bạn có thể chỉ muốn thu hẹp cấu hình tự động cần thiết cho ứng dụng của mình. Để xem danh sách đầy đủ các cấu hình tự động được bao gồm, chỉ cần chạy đăng nhập org.springframework.boot.autoconfigureở chế độ GỠ LỖI ( logging.level.org.springframework.boot.autoconfigure=DEBUGtrong application.properties). Một tùy chọn khác là chạy ứng dụng khởi động mùa xuân với --debugtùy chọn:java -jar myproject-0.0.1-SNAPSHOT.jar --debug

Sẽ có một cái gì đó như thế này trong đầu ra:

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

Kiểm tra danh sách này và chỉ bao gồm các cấu hình tự động bạn cần:

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

Mã đã được sao chép từ bài đăng trên blog này .


1
bạn đo cái này chưa ??? Nó có nhanh hơn nhiều không ?? Theo tôi đây là một trường hợp đặc biệt, quan trọng hơn nhiều để đảm bảo rằng mùa xuân tác phẩm bộ nhớ cache bối cảnh thử nghiệm
idmitriev

@idmitriev Tôi vừa đo điều này trên ứng dụng của mình và ứng dụng của tôi khởi động ở 53 giây, so với khi không loại trừ các lớp tự động định cấu hình là 73 giây. Tuy nhiên, tôi đã loại trừ nhiều lớp hơn được liệt kê ở trên.
apkisbossin

Rất vui khi nhập tất cả cấu hình. Làm thế nào để đối phó với BatchConfigurerConfiguration.JpaBatchConfiguration người ta nên thêm phần phụ thuộc vào projet? Làm thế nào để đối phó với các phương thức được tham chiếu như ConfigurationPropertiesRebinderAutoConfiguration # configurationPropertiesBeans?
dùng1767316

Làm thế nào để đối phó với các lớp cấu hình riêng?
user1767316

44

Câu trả lời được bình chọn nhiều nhất cho đến nay không sai, nhưng nó không đi vào chiều sâu mà tôi muốn xem và không cung cấp bằng chứng khoa học. Nhóm Spring Boot đã trải qua một bài tập để giảm thời gian khởi động cho Boot 2.0 và vé 11226 chứa rất nhiều thông tin hữu ích. Có cả một vé 7939 mở để thêm thông tin thời gian để đánh giá điều kiện, nhưng nó dường như không có ETA cụ thể.

Phương pháp hữu ích và phương pháp nhất để gỡ lỗi khởi động Boot đã được thực hiện bởi Dave Syer. https://github.com/dsyer/spring-boot-startup-bench

Tôi cũng có một trường hợp sử dụng tương tự, vì vậy tôi đã sử dụng phương pháp đo điểm chuẩn vi mô của Dave với JMH và chạy với nó. Kết quả là dự án chuẩn khởi động . Tôi đã thiết kế nó để nó có thể được sử dụng để đo thời gian khởi động cho bất kỳ ứng dụng Spring Boot nào, bằng cách sử dụng jar thực thi được tạo ra bởi tác vụ Gradle bootJar(trước đây được gọi bootRepackagetrong Boot 1.5). Hãy sử dụng nó và cung cấp phản hồi.

Những phát hiện của tôi như sau:

  1. Vấn đề CPU. Rất nhiều.
  2. Bắt đầu JVM với -Xverify: không có tác dụng nào đáng kể.
  3. Việc loại trừ các cấu hình tự động không cần thiết sẽ giúp ích.
  4. Dave đã đề xuất đối số JVM -XX: TieredStopAtLevel = 1 , nhưng các thử nghiệm của tôi không cho thấy sự cải thiện đáng kể với điều đó. Ngoài ra, -XX:TieredStopAtLevel=1có thể sẽ làm chậm yêu cầu đầu tiên của bạn.
  5. Đã có báo cáo về việc phân giải tên máy chủ bị chậm, nhưng tôi không thấy đó là vấn đề đối với các ứng dụng tôi đã thử nghiệm.

1
@ user991710 Không chắc nó bị hỏng như thế nào, nhưng nó đã được sửa rồi. Cảm ơn vì báo cáo.
Abhijit Sarkar,

2
Để thêm vào điều này, bạn có thể vui lòng thêm một ví dụ về cách ai đó có thể sử dụng điểm chuẩn của bạn với một ứng dụng tùy chỉnh không? Nó có phải được thêm vào như một dự án tương tự minimal, hay chỉ cần cung cấp lọ? Tôi đã cố gắng làm điều trước đây nhưng không đi được xa lắm.
user991710 19/07/18

1
Không chạy -Xverify:nonetrên sản xuất vì nó phá vỡ xác minh mã và bạn có thể gặp rắc rối. -XX:TieredStopAtLevel=1là OK nếu bạn chạy một ứng dụng trong một khoảng thời gian nhỏ (vài giây), nếu không nó sẽ kém hiệu quả hơn vì nó sẽ cung cấp cho JVM những tối ưu hóa đang hoạt động lâu dài.
loicmathieu

3
tài liệu oracle liệt kê Use of -Xverify:none is unsupported.nó có nghĩa là gì?
sakura

1
Nhiều nhóm (chắc chắn là Oracle UCP, nhưng trong thử nghiệm của tôi cũng có Hikari và Tomcat) mã hóa dữ liệu trong nhóm. Tôi thực sự không biết liệu họ đang mã hóa thông tin kết nối hay gói luồng. Bất kể, mã hóa sử dụng tạo số ngẫu nhiên và do đó, có một nguồn entropy thông lượng cao, khả dụng cao tạo ra sự khác biệt đáng chú ý về hiệu suất.
Daniel

18

Spring Boot 2.2.M1 đã thêm tính năng hỗ trợ Lazy Initialization trong Spring Boot.

Theo mặc định, khi một ngữ cảnh ứng dụng đang được làm mới, mọi bean trong ngữ cảnh sẽ được tạo và các phụ thuộc của nó được đưa vào. Ngược lại, khi định nghĩa bean được cấu hình để khởi tạo một cách lười biếng, nó sẽ không được tạo và các phụ thuộc của nó sẽ không được đưa vào cho đến khi nó cần.

Bật khởi tạo Lazy được đặt spring.main.lazy-initializationthành true

Khi nào nên kích hoạt Lazy Initialization Khởi tạo lười biếng có thể cung cấp những cải tiến đáng kể về thời gian khởi động nhưng cũng có một số nhược điểm đáng chú ý và điều quan trọng là phải bật nó một cách cẩn thận

Để biết thêm chi tiết, vui lòng kiểm tra Doc


3
nếu bạn kích hoạt tính năng khởi tạo lười biếng, quá trình tải lần đầu tiên diễn ra rất nhanh, nhưng khi khách hàng truy cập lần đầu tiên, nó có thể nhận thấy một số độ trễ. Tôi thực sự khuyên bạn nên điều này cho sự phát triển chứ không phải cho sản xuất.
Isuru Dewasurendra

Như @IsuruDewasurendra đã đề xuất, đó không phải là cách được khuyến nghị, nó có thể làm tăng đáng kể độ trễ khi ứng dụng bắt đầu tải.
Narendra Jaggi

Nó chỉ đá cái lon xuống đường.
Abhijit Sarkar

10

Như được mô tả trong câu hỏi / câu trả lời này, tôi nghĩ cách tiếp cận tốt nhất là thay vì chỉ thêm những thứ bạn nghĩ mình cần, hãy loại trừ những phần phụ thuộc mà bạn biết rằng bạn không cần.

Xem: Giảm thiểu thời gian khởi động Spring Boot

Tóm tắt:

Bạn có thể xem những gì đang diễn ra dưới các bìa và bật ghi nhật ký gỡ lỗi đơn giản như chỉ định --debug khi khởi động ứng dụng từ dòng lệnh. Bạn cũng có thể chỉ định debug = true trong application.properties của mình.

Ngoài ra, bạn có thể đặt cấp độ ghi nhật ký trong application.properties đơn giản như:

logging.level.org.springframework.web: GỠ LỖI logging.level.org.hibernate: LỖI

Nếu bạn phát hiện một mô-đun được cấu hình tự động mà bạn không muốn, nó có thể bị vô hiệu hóa. Các tài liệu cho việc này có thể được tìm thấy ở đây: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration

Một ví dụ sẽ giống như sau:

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

4

Vâng, có toàn bộ danh sách các hành động có thể được mô tả ở đây: https://spring.io/blog/2018/12/12/how-fast-is-spring

Tôi sẽ đặt những ghi chú quan trọng nhất từ ​​phía Spring (điều chỉnh một chút):

  • Loại trừ Classpath khỏi trình khởi động web Spring Boot:
    • Trình xác thực Hibernate
    • Jackson (nhưng bộ truyền động Spring Boot phụ thuộc vào nó). Sử dụng Gson nếu bạn cần kết xuất JSON (chỉ hoạt động với MVC ngoài hộp).
    • Logback: sử dụng slf4j-jdk14 để thay thế
  • Sử dụng trình chỉ mục-ngữ cảnh-mùa xuân. Nó sẽ không thêm nhiều, nhưng mỗi chút sẽ giúp ích.
  • Không sử dụng thiết bị truyền động nếu bạn có thể không sử dụng.
  • Sử dụng Spring Boot 2.1 và Spring 5.1. Chuyển sang 2.2 và 5.2 khi chúng khả dụng.
  • Sửa vị trí của (các) tệp cấu hình Spring Boot với spring.config.location(đối số dòng lệnh hoặc thuộc tính Hệ thống, v.v.). Ví dụ để thử nghiệm trong IDE:spring.config.location=file://./src/main/resources/application.properties .
  • Tắt JMX nếu bạn không cần spring.jmx.enabled=false(đây là mặc định trong Spring Boot 2.2)
  • Đặt định nghĩa bean lười theo mặc định. Có một cờ mới spring.main.lazy-initialization=truetrong Spring Boot 2.2 (sử dụngLazyInitBeanFactoryPostProcessor cho Spring cũ hơn).
  • Mở gói chất béo và chạy với một classpath rõ ràng.
  • Chạy JVM với -noverify. Cũng nên xem xét -XX:TieredStopAtLevel=1(điều đó sẽ làm chậm JIT sau này do thời gian khởi động đã tiết kiệm được).

Đã đề cập LazyInitBeanFactoryPostProcessor(bạn có thể sử dụng nó cho Spring 1.5 nếu bạn không thể áp dụng cờ spring.main.lazy-initialization=truecó sẵn từ 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);
      }
  }
}

Bạn cũng có thể sử dụng (hoặc viết của riêng bạn - nó đơn giản) để phân tích thời gian khởi tạo bean: https://github.com/lwaddicor/spring-startup-analysis

Hy vọng nó giúp!


0

Trong trường hợp của tôi, có quá nhiều điểm ngắt. Khi tôi nhấp vào "Mute Breakpoints" và khởi động lại ứng dụng ở chế độ gỡ lỗi, ứng dụng đã khởi động nhanh hơn 10 lần.


-1

Nếu bạn đang cố gắng tối ưu hóa quá trình phát triển để kiểm tra thủ công, tôi thực sự khuyên bạn nên sử dụng devtools .

Các ứng dụng sử dụng spring-boot-devtools sẽ tự động khởi động lại bất cứ khi nào các tệp trên classpath thay đổi.

Chỉ cần biên dịch lại - và máy chủ sẽ tự khởi động lại (đối với Groovy, bạn chỉ cần cập nhật tệp nguồn). nếu bạn đang sử dụng IDE (ví dụ: 'vscode'), nó có thể tự động biên dịch các tệp java của bạn, vì vậy chỉ cần lưu tệp java là có thể khởi động lại máy chủ một cách gián tiếp - và Java trở nên liền mạch như Groovy về mặt này.

Cái hay của phương pháp này là việc khởi động lại tăng dần làm ngắn mạch một số bước khởi động từ đầu - vì vậy dịch vụ của bạn sẽ được sao lưu và chạy nhanh hơn nhiều!


Thật không may, điều này không giúp ích cho thời gian khởi động để triển khai hoặc kiểm tra đơn vị tự động.


-1

CẢNH BÁO: Nếu bạn không sử dụng DDL Hibernate để tạo lược đồ DB tự động và bạn không sử dụng bộ đệm L2, câu trả lời này KHÔNG áp dụng cho bạn. Di chuyển về phía trước.

Phát hiện của tôi là Hibernate thêm thời gian đáng kể cho việc khởi động ứng dụng. Tắt bộ nhớ cache L2 và khởi tạo cơ sở dữ liệu dẫn đến khởi động ứng dụng Spring Boot nhanh hơn. BẬT bộ nhớ cache cho quá trình sản xuất và tắt nó cho môi trường phát triển của bạn.

application.yml:

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

Kết quả kiểm tra:

  1. Bộ nhớ đệm L2 đang BẬT và 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. Bộ nhớ đệm L2 đang TẮT và 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)

Bây giờ tôi tự hỏi tôi sẽ làm gì với tất cả thời gian rảnh rỗi này


hibernate.hbm2ddl.auto = cập nhật không liên quan gì đến bộ nhớ đệm l2. ddl .. = update chỉ định quét lược đồ cơ sở dữ liệu hiện tại và tính toán sql cần thiết để cập nhật giản đồ để phản ánh các thực thể của bạn. 'Không có' không thực hiện xác minh này (cũng không cố gắng cập nhật lược đồ). Các phương pháp hay nhất là sử dụng một công cụ như liquibase, nơi bạn sẽ xử lý các thay đổi lược đồ của mình và bạn cũng có thể theo dõi chúng.
Radu Toader

@RaduToader câu hỏi này và câu trả lời của tôi là về việc tăng tốc thời gian khởi động Spring Boot. Họ không liên quan gì đến cuộc thảo luận Hibernate DDL và Liquibase; những công cụ này đều có ưu và nhược điểm của chúng. Quan điểm của tôi là chúng ta có thể tắt cập nhật lược đồ DB và chỉ bật khi cần thiết. Hibernate mất thời gian đáng kể khi khởi động ngay cả khi mô hình không thay đổi kể từ lần chạy cuối cùng (để so sánh lược đồ DB với lược đồ được tạo tự động). Điểm tương tự cũng đúng với bộ nhớ đệm L2.
naXa

vâng, tôi biết điều đó, nhưng quan điểm của tôi là điều này hơi nguy hiểm nếu không giải thích nó thực sự làm gì. Bạn có thể rất dễ dàng kết thúc với db của bạn trống rỗng.
Radu Toader

@RaduToader Có một liên kết đến trang tài liệu về khởi tạo DB trong câu trả lời của tôi. Bạn đã đọc nó? Nó chứa một hướng dẫn đầy đủ, liệt kê tất cả các công cụ phổ biến nhất (Hibernate và Liquibase, cũng như JPA và Flyway). Ngoài ra hôm nay tôi thêm một cảnh báo rõ ràng vào đầu câu trả lời của tôi. Bạn có nghĩ rằng tôi cần bất kỳ thay đổi nào khác để giải thích hậu quả không?
naXa

Hoàn hảo. Cảm ơn bạn
Radu Toader

-3

Tôi thấy lạ là không ai đề xuất những tối ưu hóa này trước đây. Dưới đây là một số mẹo chung về tối ưu hóa quá trình xây dựng và khởi động dự án khi phát triển:

  • loại trừ các thư mục phát triển khỏi trình quét chống vi-rút:
    • thư mục dự án
    • xây dựng thư mục đầu ra (nếu nó nằm ngoài thư mục dự án)
    • Thư mục chỉ số IDE (ví dụ: ~ / .IntelliJIdea2018.3)
    • thư mục triển khai (ứng dụng web trong Tomcat)
  • nâng cấp phần cứng. sử dụng CPU và RAM nhanh hơn, kết nối internet tốt hơn (để tải xuống phần phụ thuộc) và kết nối cơ sở dữ liệu, hãy chuyển sang SSD. một thẻ video không quan trọng.

CẢNH BÁO

  1. tùy chọn đầu tiên đi kèm với cái giá của việc giảm bảo mật.
  2. lựa chọn thứ hai tốn tiền (rõ ràng).

Câu hỏi là về cải thiện thời gian khởi động, không phải thời gian biên dịch.
ArtOfWarfare

@ArtOfWarfare đọc lại câu hỏi. câu hỏi nêu vấn đề là "Tôi không hài lòng vì nó mất nhiều [thời gian] như vậy, chủ yếu là vì nó phá vỡ dòng phát triển". Tôi cảm thấy đây là một vấn đề chính và đã giải quyết nó trong câu trả lời của tôi.
naXa

-9

Đối với tôi, có vẻ như bạn đang sử dụng cài đặt cấu hình sai. Bắt đầu bằng cách kiểm tra myContainer và các xung đột có thể xảy ra. Để xác định ai đang sử dụng nhiều tài nguyên nhất, bạn phải kiểm tra bản đồ bộ nhớ (xem số lượng dữ liệu!) Cho mỗi phần phụ thuộc tại một thời điểm - và điều đó cũng mất rất nhiều thời gian ... (và đặc quyền SUDO). Nhân tiện: bạn có thường kiểm tra mã chống lại các phụ thuộc không?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.