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.
	 * <p>
	 * 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.867s18.160s/19.291s
第二次测试耗时19.407s/20.563s18.388s/19.496s
第三次测试耗时21.734s/22.90318.308s/19.721s
第四次测试耗时19.908s/21.090s18.576s/19.708s
第五次测试耗时19.456s/20.650s18.418s/19.699s
平均耗时20.049s/21.215s18.370s/19.583s

   表格说明:此表基于上述环境测试的真实值,平均耗时取有效小数5位。19.742s/20.867s :表示 应用启动耗时19.742秒,JVM启动耗时20.867秒。
  通过上述测试数据发现使用 @EnableAutoConfiguration + @Configuration 注解 可以提高应用程序启动的性能。

二、将 Servlet 容器由 Tomcat 变成 Undertow

  默认情况下 Spring Boot 使用 Tomcat 来作为内嵌的 Servlet 容器,我们可以将 Web 服务器切换到 Undertow 来提高应用性能。Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。接下来我们使用 Jmeter 分别测试使用 Tomcat 容器 和 Undertow 容器在同一个查询数据库信息的接口下的各自的并发量。设置 JVM 参数:-server -XX:+PrintGCDetails -Xms2048m -Xmx2048m,测试数据如下。

测试项目Tomcat 容器Undertow 容器
第一次吞吐量测试315.3/sec395.0/sec
第二次吞吐量测试421.6/sec453.2/sec
第三次吞吐量测试327.8/sec448.8/sec
第四次吞吐量测试351.4/sec340.0/sec
第五次吞吐量测试431.1/sec433.2/sec
平均吞吐量369.44/sec414.04/sec

   表格说明:此表基于上述环境测试的真实值,平均吞吐量取有效小数5位。
  由上表数据可以看出将 Tomcat 容器换为 Undertow 容器将会提高应用性能。

三、JVM 调优

  关于 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 512MTomcat 2048M
第一次吞吐量测试300.5/sec315.3/sec
第二次吞吐量测试293.5/sec421.6/sec
第三次吞吐量测试282.6/sec327.8/sec
第四次吞吐量测试302.8/sec351.4/sec
第五次吞吐量测试309.5/sec431.1/sec
平均吞吐量297.78/sec369.44/sec

   表格说明:此表基于上述环境测试的真实值,平均吞吐量取有效小数5位。
  数据说明JVM 参数 `-server -XX:+PrintGCDetails -Xms2048m -Xmx2048m` 比 `-server -XX:+PrintGCDetails -Xms512m -Xmx512m` 要优。进行 JVM 调优是一个谨慎细致的过程,需要慢慢的试,直到当前最优为止。
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐