SpringBoot 是一个快速开发框架,能够快速的整合第三方框架,简化XML配置,全部采用注解形式,内置Tomcat容器,帮助开发者能够实现快速开发,SpringBoot的Web组件 默认集成的是SpringMVC框架。
尽管 SpringBoot 拥有这么多的优点,但也存在性能问题,这并不和它拥有如此多的优点相冲突,应用程序性能只有更优,没有最优。
对于 SpringBoot 性能优化可以从注解 @SpringBootApplication
上优化,Servlet 容器上优化, JVM 上优化等等。我们分别从这三方面进行优化。
测试机器信息:
OS :Windows 10 专业版
CPU :Intel® Core™ i5-3230M @2.6GHz
RAM :8 GB
ROM :120G SSD + 500G HDD
JDK :Java version “1.8.0_171”
Eclipse :Eclipse Java EE IDE for Web Developers 4.7.3a
MySQL :MySQL Version 5.6.15
@SpringBootApplication
@SpringBootApplication
是Springboot 整合的一个复合注解,作用相当于 @Configuration
+ @EnableAutoConfiguration
+ @ComponentScan
,这个可以查看 @SpringBootApplication
源码得知,其中有一句 This is a convenience annotation that is equivalent to declaring {@code @Configuration},{@code @EnableAutoConfiguration} and {@code @ComponentScan}
说的就是这个意思。
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
*
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class>[] scanBasePackageClasses() default {};
}
由于其中包括有包扫描的注解 @ComponentScan
,这会导致项目启动时间变长(启动一个大的应用程序或做大量的集成测试启动应用程序时,影响会特别明显),会加载一些多余的实例(Beans),也会增加 CPU 消耗。
所以可以将 @SpringBootApplication
注解改为 @EnableAutoConfiguration
+ @Configuration
+ 在我们需要的 bean 上进行显式配置注解。
通过实际使用 @SpringBootApplication
注解 和 使用 @EnableAutoConfiguration
+ @Configuration
注解分别各测试启动 5 次 记录实际启动时间,最后取 5 次的平均时间来比较性能的变化,JVM 各参数默认,测试的详细数据见下。
测试项目 | 使用 @SpringBootApplication 注解 |
使用 @EnableAutoConfiguration + @Configuration 注解 |
---|---|---|
第一次测试耗时 | 19.742s/20.867s | 18.160s/19.291s |
第二次测试耗时 | 19.407s/20.563s | 18.388s/19.496s |
第三次测试耗时 | 21.734s/22.903 | 18.308s/19.721s |
第四次测试耗时 | 19.908s/21.090s | 18.576s/19.708s |
第五次测试耗时 | 19.456s/20.650s | 18.418s/19.699s |
平均耗时 | 20.049s/21.215s | 18.370s/19.583s |
默认情况下 Spring Boot 使用 Tomcat 来作为内嵌的 Servlet 容器,我们可以将 Web 服务器切换到 Undertow 来提高应用性能。Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。接下来我们使用 Jmeter
分别测试使用 Tomcat 容器 和 Undertow 容器在同一个查询数据库信息的接口下的各自的并发量。设置 JVM 参数:-server -XX:+PrintGCDetails -Xms2048m -Xmx2048m
,测试数据如下。
测试项目 | Tomcat 容器 | Undertow 容器 |
---|---|---|
第一次吞吐量测试 | 315.3/sec | 395.0/sec |
第二次吞吐量测试 | 421.6/sec | 453.2/sec |
第三次吞吐量测试 | 327.8/sec | 448.8/sec |
第四次吞吐量测试 | 351.4/sec | 340.0/sec |
第五次吞吐量测试 | 431.1/sec | 433.2/sec |
平均吞吐量 | 369.44/sec | 414.04/sec |
关于 JVM 调优需要了解服务器基本参数配置,JVM 基本组成结构,垃圾回收算法,垃圾回收机制,JVM 调优工具,调优经验等等等,这里不详细列举。
这里就以 JVM 参数 -server -XX:+PrintGCDetails -Xms512m -Xmx512m
和 -server -XX:+PrintGCDetails -Xms2048m -Xmx2048m
为例,还是测试吞吐量,对于参数 -server -XX:+PrintGCDetails -Xms2048m -Xmx2048m
的数据从Tomcat 容器下吞吐量的测试得来,就不做重复测试了,然后测试参数 -server -XX:+PrintGCDetails -Xms512m -Xmx512m
在 Tomcat 容器下的吞吐量,测试数据见下表。
测试项目 | Tomcat 512M | Tomcat 2048M |
---|---|---|
第一次吞吐量测试 | 300.5/sec | 315.3/sec |
第二次吞吐量测试 | 293.5/sec | 421.6/sec |
第三次吞吐量测试 | 282.6/sec | 327.8/sec |
第四次吞吐量测试 | 302.8/sec | 351.4/sec |
第五次吞吐量测试 | 309.5/sec | 431.1/sec |
平均吞吐量 | 297.78/sec | 369.44/sec |