vlambda博客
学习文章列表

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

Spring Boot Microservice Performance Tuning

在上一章中,我们了解了 Java 虚拟机 (JVM)。从 JVM 的内部结构和 Java 的类加载机制开始,我们了解了 Java 中的内存管理是如何执行的。本章的最后一节重点介绍垃圾收集和 JVM 调优。这一章充满了应用程序性能优化的非常重要的细节。

在本章中,我们将着手解决性能问题。方法是开发微服务。微服务目前正在软件开发行业蓬勃发展。围绕微服务和相关关键字有很多讨论。这种方法基本上适用于应用程序架构级别,以调整应用程序的性能。它描述了我们如何通过以不同的方式设置架构来提高应用程序性能。我们将在本章中介绍以下主题:

  • Spring Boot configuration
  • Metrics with the Spring Boot Actuator
  • Health checks
  • Microservices using Spring Boot
  • Microservices with Spring Cloud
  • Spring microservice configuration example
  • Monitoring microservices with Spring Boot admin
  • Spring Boot performance tuning

Spring Boot configuration

在本节中,我们将专注于让 Spring Boot 为我们工作。在开始配置 Spring Boot 之前,我们将了解 Spring Boot 是什么,为什么要使用它,以及 Spring Boot 带来了什么。我们将快速移动到如何做它的一部分。

What is Spring Boot?

软件开发过程必须更快、更准确、更健壮。软件团队被要求开发快速原型,以向潜在客户展示应用程序的功能。这同样适用于生产级应用。以下是软件架构师关注提高开发团队效率的几个领域:

  • Use the right set of tools, which includes frameworks, IDEs, and build tools
  • Reduce code clutter
  • Reduce the amount of time spent writing repetitive code
  • Spend the majority of the time implementing business features

让我们想一想。我们为什么要讨论这个?原因是这是 Spring Boot 的基础。这些想法是创建任何有助于团队提高生产力的框架或工具的基石。发现 Spring Boot 的原因完全相同——提高生产力!

使用 Spring Boot,可以轻松创建由 Spring Framework 提供支持的生产级应用程序。它还可以轻松创建具有最小挑战的生产就绪服务。 Spring Boot 通过对 Spring 框架的自以为是的观点,帮助新用户和现有用户快速完成他们的生产任务。 Spring Boot 是一种便于创建独立 Java 应用程序的工具,可以使用 java -jar 命令运行,或者可以部署到 Web 服务器的 Web 应用程序。 Spring Boot 设置与命令行工具捆绑在一起以运行 Spring 程序。

Spring Boot 的主要目标是:

  • To gain an extremely fast experience to get started with Spring-powered projects
  • Broad accessibility
  • Major support from out-of-the-box configuration
  • Flexibility to deviate from Spring default as the need may arise
  • That it does not generate any code
  • That it does not require XML configuration

除了之前列出的主要特性之外,Spring Boot 还提供了对非功能特性的支持,如下所列:

  • Support for versioning and configuration for widely known and used frameworks
  • Support for application security
  • Support for monitoring application health check parameters
  • Support for monitoring of performance metrics
  • Support for externalized configurations

尽管 Spring Boot 为主要和非功能特性提供了默认值,但它足够灵活,允许开发人员使用他们选择的框架、服务器和工具。

Spring Initializr

Spring Boot 应用程序可以通过多种方式启动。其中一种方法是使用基于 Eclipse 的 Spring Tools Suite IDE (https://spring.io/tools/sts )。另一种方法是使用 https://start.spring.io,也称为 Spring Initializr。首先,Spring Initializr 不是 Spring Boot 或等效的。 Spring Initializr 是一个工具,它具有简单的 Web UI 支持来配置 Spring Boot 应用程序。它可以被认为是一个快速启动生成 Spring 项目的工具。它提供了可以为自定义扩展的 API,以便生成项目。

Spring Initializr 工具提供了一个配置结构来定义依赖项列表、支持的 Java 和 Spring Boot 版本以及支持的依赖项版本控制。

基本上,Spring Initializr 根据提供的配置创建一个初始 Spring 项目,并允许开发人员以 ZIP 文件的形式下载项目。以下是要遵循的步骤:

  1. Navigate to https://start.spring.io/.
  2. Choose the dependency management tool from Maven or Gradle.
  3. Choose the JVM-based programming language from Java, Kotlin, and Groovy.
  4. Choose the Spring Boot version to be used.
  5. Provide the Group artifact by inputting the group name as com.packt.springhighperformance.
  1. Input Artifact, which is the artifact ID for the Maven project. This will become the name of the project WAR or JAR file to be deployed or executed.
  2. Choose a packaging type from Jar and War.
  3. Click on the Switch to the full version link. This will open up a list of starter projects to choose from. The starter project will be explained in detail in the following section.
  4. Once we have chosen the starters or dependencies, click on the Generate Project button. This will download the ZIP file containing the initial project configuration.

以下是带有一些配置的 Spring Initializr 屏幕:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

完成后,将生成类似于以下屏幕截图所示的文件夹结构:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

Spring Initializr 还支持命令行界面来创建 Spring 项目配置。以下命令可用于生成项目配置:

> curl https://start.spring.io/starter.zip -d dependencies=web,jpa -d bootVersion=2.0.0 -o ch-09-spring-boot-example-1.zip

如前所述,Spring Initializr 支持与 IDE 的集成。它与 Eclipse/STS、IntelliJ 终极版和带有 NB SpringBoot 插件的 NetBeans 很好地集成在一起。

Starters with Maven

在上一节中,我们查看了 Spring Initializr 工具。是时候快速查看 Spring Boot 支持的启动器或依赖项了。

随着项目复杂性的增加,依赖管理变得具有挑战性。建议不要手动管理复杂项目的依赖关系。 Spring Boot 启动器修复了类似的问题。 Spring Boot starters 是一组依赖描述符,可以使用 starter POM 包含在 Spring 驱动的应用程序中。它消除了为 Spring 和相关库寻找示例代码和复制/粘贴大量依赖项描述符的需要。例如,如果我们想使用 Spring 和 JPA 开发应用程序,我们可以在项目中包含 spring-boot-data-jpa-starter 依赖项。 spring-boot-data-jpa-starter 是启动器之一。启动器遵循统一的命名模式,例如 spring-boot-starter-*,其中 * 表示应用程序的类型。

以下是一些 Spring Boot 应用程序启动器的列表:

Name Description
spring-boot-starter Core starter provides auto-configuration and logging support.
spring-boot-starter-activemq JMS messaging starter using Apache ActiveMQ.
spring-boot-starter-amqp Spring AMQP and Rabbit MQ starter.
spring-boot-starter-aop Spring AOP and AspectJ starter.
spring-boot-starter-artemis JMS messaging starter using Apache Artemis.
spring-boot-starter-batch Spring Batch starter.
spring-boot-starter-cache Spring Framework's caching support.
spring-boot-starter-cloud-connectors Starter providing support for simplified connections with cloud services using Spring Cloud Connectors in cloud platforms such as Cloud Foundry and Heroku.
spring-boot-starter-data-elasticsearch Starter with support for elasticsearch and analytics engine, and Spring Data Elasticsearch.
spring-boot-starter-data-jpa Spring Data JPA with Hibernate.
spring-boot-starter-data-ldap Spring Data LDAP.
spring-boot-starter-data-mongodb MongoDB document-oriented database and Spring Data MongoDB.
spring-boot-starter-data-redis Redis key-value data store with Spring Data Redis and the Lettuce client.
spring-boot-starter-data-rest Starter providing support for exposing Spring Data repositories over REST using Spring Data REST.
spring-boot-starter-data-solr Apache Solr search platform with Spring Data Solr.
spring-boot-starter-freemarker Starter supports building MVC web applications using the FreeMarker views.
spring-boot-starter-groovy-templates Starter supporting building MVC web applications using the Groovy templates views.
spring-boot-starter-integration Spring Integration.
spring-boot-starter-jdbc JDBC with the Tomcat JDBC connection pool.
spring-boot-starter-jersey Starter supporting building RESTful web applications using JAX-RS and Jersey. It is an alternative to spring-boot-starter-web starter.
spring-boot-starter-json Starter supporting JSON manipulation.
spring-boot-starter-mail Starter supporting the use of Java Mail and Spring Framework’s email-sending support.
spring-boot-starter-quartz Starter for using Spring Boot Quartz.
spring-boot-starter-security Spring Security starter.
spring-boot-starter-test Support for Spring Boot applications with libraries including JUnit, Hamcrest, and Mockito.
spring-boot-starter-thymeleaf Supports building MVC web applications using the Thymeleaf views.
spring-boot-starter-validation Starter supporting Java Bean Validation with Hibernate Validator.
spring-boot-starter-web Supports building web, including RESTful, applications using Spring MVC. It uses Tomcat as the default embedded container.
spring-boot-starter-web-services Supports use of Spring Web Services.
spring-boot-starter-websocket Supports building WebSocket applications using Spring Framework's WebSocket support.

spring-boot-starter-actuator 是 Spring Boot 的 Actuator 工具的生产启动器,它提供对生产就绪功能的支持,例如应用程序监控、健康检查、日志记录和 bean。< /跨度>

以下列表包括一些 Spring Boot 的技术入门:

Name Description
spring-boot-starter-jetty Support for Jetty as the embedded servlet container. This is an alternative to spring-boot-starter-tomcat.
spring-boot-starter-log4j2 Starter supporting Log4j 2 for logging. This is an alternative to spring-boot-starter-logging.
spring-boot-starter-logging This is the default logging starter using logback.
spring-boot-starter-tomcat This is the default servlet container starter used for spring-boot-starter-web. It uses Tomcat as the embedded server.
spring-boot-starter-undertow This is an alternative to spring-boot-starter-tomcat starter. It uses Undertow as the embedded server.
spring-boot-starter-cache Spring Framework's caching support.

Creating your first Spring Boot application

在本节中,我们将了解开发 Spring Boot 应用程序的先决条件。我们将开发一个小型 Spring Boot 应用程序来了解 Spring Boot 应用程序所需的配置以及每个配置的重要性。

以下是使用 Spring Boot 的先决条件列表:

  • Java 8 or 9
  • Spring 5.0.4 or later

Spring Boot 支持:

  • Maven 3.2+ and Gradle 4 for dependency management and explicit builds
  • Tomcat 8.5, Jetty 9.4, and Undertow 1.4

Spring Boot 应用程序可以部署到任何 servlet 3.0+ 兼容的 servlet 容器。

开发 Spring Boot 应用程序的第一步是安装 Spring Boot。它非常容易设置。它可以像其他标准 Java 库一样设置。要安装 Spring Boot,我们需要在类路径中包含适当的 spring-boot-*.jar 库文件。任何 IDE 或文本编辑器都可以使用,因为 Spring Boot 不需要任何专门的工具。

虽然我们可以在应用程序类路径中复制所需的 Spring Boot JAR 文件,但建议使用构建工具,例如 Maven 或 Gradle,进行依赖管理。

Spring Boot 依赖项使用的 Maven groupIdorg.springframework.boot。对于 Spring Boot 应用程序,Maven POM 文件继承 spring-boot-starter-parent 项目。 Spring Boot 定义了启动项目,它被定义为 Spring Boot 应用程序依赖项中的一个依赖项。

让我们通过执行以下步骤开始创建我们的第一个 Spring Boot 应用程序:

  1. Create a kickstarter application using Spring Initializr.
  2. Choose Maven as the build and dependency management tool.
  3. Choose the appropriate Spring Boot version.
  4. Choose the packaging type as War.
  5. For the sake of simplicity, we will not include a JPA starter in the application. We will include a web module only to demonstrate the one request-response flow.
  6. Download and import the project into STS or Eclipse.
  7. In STS, you can run the application as a Spring Boot application whereas in Eclipse, you can choose to run the application as a Java Application.

现在让我们看一下代码片段。以下是示例 Maven POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.packt.springhighperformance.ch09</groupId>
  <artifactId>ch-09-boot-example</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>boot-example</name>
  <description>Demo project for Spring boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-
    8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.M9</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

上述配置文件中值得注意的配置之一是父依赖项。如前所述,所有 Spring Boot 应用程序都使用 spring-boot-starter-parent 作为 pom.xml 文件中的父依赖。

父 POM 帮助管理子项目和模块的以下内容:

  • Java version
  • Version management for included dependencies
  • The default configuration for the plugin

Spring Boot parent starter 将 Spring Boot 依赖项定义为 parent POM。因此,它从 Spring Boot 依赖项中继承了依赖项管理功能。它将默认 Java 版本定义为 1.6,但在项目级别,我们可以将其更改为 1.8,如前面的代码示例所示。

除了默认的 POM 文件,Spring Boot 还创建了一个 Java 类,作为应用程序的启动器。以下是示例 Java 代码:

package com.packt.springhighperformance.ch09;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BootExampleApplication {

  public static void main(String[] args) {
    SpringApplication.run(BootExampleApplication.class, args);
  }
}

SpringApplication 是一个负责引导 Spring Boot 应用程序的类。

Spring Boot 应用程序开发人员习惯于使用 @Configuration@EnableAutoConfiguration@ComponentScan 注释来注释主应用程序类。以下是每个注释的简要说明:

  • @Configuration: This is a Spring annotation and not specific to Spring Boot applications. It indicates that the class is the source for bean definitions.
  • @EnableAutoConfiguration: This one is a Spring Boot-specific annotation. The annotation enables the application to add beans from the classpath definitions.
  • @ComponentScan: This annotation tells the Spring application to search for components, configurations, and services in the search path provided.

以下是 @SpringBootApplication 注解的定义:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
......

查看前面的代码,很明显 @SpringBootApplication 是作为一个方便的注解来定义 Spring Boot 应用程序的,而不是声明三个注解。

以下块显示了 Spring Boot 应用程序启动时的日志输出:


  . ____ _ __ _ _
 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot :: (v2.0.0.RELEASE)

2018-05-23 16:29:21.382 INFO 32268 --- [ main] c.p.s.ch09.BootExampleApplication : Starting BootExampleApplication on DESKTOP-4DS55MC with PID 32268 (E:\projects\spring-high-performance\ch-09\boot-example\target\classes started by baps in E:\projects\spring-high-performance\ch-09\boot-example)
2018-05-23 16:29:21.386 INFO 32268 --- [ main] c.p.s.ch09.BootExampleApplication : No active profile set, falling back to default profiles: default
2018-05-23 16:29:21.441 INFO 32268 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@58ce9668: startup date [Wed May 23 16:29:21 IST 2018]; root of context hierarchy
2018-05-23 16:29:23.854 INFO 32268 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-05-23 16:29:23.881 INFO 32268 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-05-23 16:29:23.881 INFO 32268 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.28
2018-05-23 16:29:23.888 INFO 32268 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: ...
2018-05-23 16:29:24.015 INFO 32268 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-05-23 16:29:24.016 INFO 32268 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2581 ms
2018-05-23 16:29:25.011 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-05-23 16:29:25.015 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-05-23 16:29:25.016 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-05-23 16:29:25.016 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-05-23 16:29:25.016 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-05-23 16:29:25.016 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpTraceFilter' to: [/*]
2018-05-23 16:29:25.016 INFO 32268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webMvcMetricsFilter' to: [/*]
2018-05-23 16:29:26.283 INFO 32268 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/welcome]}" onto public java.lang.String com.packt.springhighperformance.ch09.controllers.MainController.helloMessage(java.lang.String)
2018-05-23 16:29:26.284 INFO 32268 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.packt.springhighperformance.ch09.controllers.MainController.helloWorld()
2018-05-23 16:29:26.291 INFO 32268 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-05-23 16:29:26.292 INFO 32268 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-05-23 16:29:26.358 INFO 32268 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-05-23 16:29:26.359 INFO 32268 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-05-23 16:29:26.410 INFO 32268 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-05-23 16:29:27.033 INFO 32268 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-05-23 16:29:27.082 INFO 32268 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-05-23 16:29:27.085 INFO 32268 --- [ main] c.p.s.ch09.BootExampleApplication : Started BootExampleApplication in 6.068 seconds (JVM running for 7.496)

此时,我们已经准备好 kickstarter Spring Boot 应用程序,但我们没有任何要呈现的 URL。因此,当您访问 http://localhost:8080 时,会显示类似于以下屏幕截图所示的页面:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

让我们定义 Spring 控制器和默认路由,并为其添加文本内容。以下是控制器类的代码片段:

package com.packt.springhighperformance.ch09.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MainController {
  
  @RequestMapping(value="/")
  @ResponseBody
  public String helloWorld() {
    return "<h1>Hello World<h1>";
  }
  
  @RequestMapping(value="/welcome")
  @ResponseBody
  public String showMessage(@RequestParam(name="name") String name) {
    return "<h1>Hello " + name + "<h1>";
  }

}

在前面的示例代码中,我们使用 @RequestMapping 注释定义了两个路由。以下是前面代码块中使用的注释列表以及简要说明:

  • The @Controller annotation indicates that the class is a controller class and may contain request mappings.
  • The @RequestMapping annotation defines an application URL that the users can navigate to in the browser.
  • The @ResponseBody annotation indicates that the method return value should be rendered on the page as the HTML content. The value parameter can take the URL path to be navigated.

以下屏幕截图显示了我们在浏览器中点击 http://localhost:8080 时显示或呈现的页面:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

我们还定义了参数化请求映射,其值为 /welcome。当我们在浏览器中导航到 URL 时,请求参数的值将反映在页面上的消息中。以下屏幕截图显示了内容的呈现方式:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

当具有这些请求映射的应用程序启动时,我们可以找到以下日志条目:

2018-03-24 10:26:26.154 INFO 11148 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3c153a1: startup date [Sat Mar 24 10:26:24 IST 2018]; root of context hierarchy
2018-03-24 10:26:26.214 INFO 11148 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.packt.springhighperformance.ch09.controllers.MainController.helloWorld()
2018-03-24 10:26:26.218 INFO 11148 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/welcome]}" onto public java.lang.String com.packt.springhighperformance.ch09.controllers.MainController.helloMessage(java.lang.String)

至此,我们的第一个带有示例请求映射的 Spring Boot 应用程序就位。本节作为 Spring Boot 应用程序开发的分步指南。在下一节中,我们将了解更多 Spring Boot 特性。

Metrics with Spring Boot Actuator

在我们进一步讨论之前,了解 Spring Boot Actuator 是什么很重要。我们将在后面的章节中介绍 Spring Boot Actuator。我们还将了解 Spring Boot Actuator 提供的开箱即用功能。我们还将通过示例来了解配置和其他必要的细节。

What is Spring Actuator?

本质上,Spring Boot Actuator 可以被认为是 Spring Boot 的一个子项目。它有助于在我们使用 Spring Boot 开箱即用开发的应用程序中引入生产级功能。需要先配置 Spring Boot Actuator,然后才能利用它公开的功能。 Spring Boot Actuator 自 2014 年 4 月 Spring Boot 首次发布以来一直可用。Spring Boot Actuator 实现了不同的 HTTP 端点,因此开发团队可以执行以下任务:

  • Application monitoring
  • Analyzing application metrics
  • Interacting with the application
  • Version information
  • Logger details
  • Bean details

Enabling Spring Boot Actuator

除了帮助引导应用程序开发之外,Spring Boot 还支持许多可在应用程序中使用的功能。这些附加功能包括但不限于监控和管理应用程序。应用程序管理和监控可以通过 HTTP 端点或使用 JMX 完成。审计、健康检查和指标也可以通过 Spring Boot 应用程序中的配置应用。这些是 spring-boot-actuator 模块提供的生产就绪功能。

这是 Spring Boot 参考文档中对 Actuator 的定义(https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready):

An actuator is a manufacturing term that refers to a mechanical device for moving or controlling something. Actuators can generate a large amount of motion from a small change.

为了利用 Spring Boot Actuator 的特性,第一步是启用它。默认情况下不启用它,我们必须添加依赖项才能启用它。在 Spring Boot 应用程序中启用 Spring Boot Actuator 非常容易。如果我们使用 Maven,我们需要在 the pom.xml 文件中添加 spring-boot-starter-actuator 依赖项用于应用程序中的依赖管理。以下是 Spring Boot Actuator 的 Maven 依赖的片段:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

如前所述,Spring Boot Actuator 通过公开或启用端点交互来启用应用程序监控。该模块有许多开箱即用的端点。它还允许开发人员创建自己的自定义端点。我们可以启用或禁用每个单独的端点。这确保端点是在应用程序中创建的,并且相应的 bean 存在于应用程序的上下文中。

可以通过 JMX 或 HTTP 公开端点来远程访问端点。通常,应用程序通过 HTTP 公开端点。端点 URL 是通过将端点 ID 与 /actuator 前缀进行映射来派生的。

以下是与技术无关的端点列表:

ID Description Enabled by default
auditevents This endpoint exposes the audio event's information. Yes
beans This endpoints shows a complete list of the Spring beans available in the application. Yes
conditions This endpoint displays the conditions that are evaluated on configuration and auto-configuration classes. Yes
configprops This endpoint shows a list of properties marked with @ConfigurationProperties. Yes
env This endpoint displays the properties from Spring's ConfigurableEnvironment. Yes
flyway The endpoint shows any flyway database migrations that might have been applied. Yes
health This endpoint shows the health information of the application. Yes
httptrace This endpoint shows the HTTP trace information. By default, it shows the last 100 HTTP request-response exchanges. Yes
info This endpoint exposes application information. Yes
loggers This endpoint shows the application logger configuration. Yes
liquibase This endpoint displays any liquibase database migrations that might have been applied. Yes
metrics This endpoint displays metrics information for the application. Yes
mappings This endpoint displays a list of all the @RequestMapping paths. Yes
scheduledtasks This endpoint shows the scheduled tasks for the application. Yes
sessions This endpoint allows retrieval and deletion of user sessions from a Spring Session-backed session store. It is not available when using Spring Session’s support for reactive web applications. Yes
shutdown This endpoint allows the application to be shutdown gracefully. No
threaddump This endpoint performs a threaddump. Yes

如果应用程序是 Web 应用程序,以下是公开的附加端点列表:

ID Description Enabled by default
heapdump This endpoint returns a compressed hprof heap dump file. Yes
jolokia This endpoint exposes JMX beans over HTTP. Yes
logfile This endpoint shows the contents of the logfile if the logging.file or logging.path is set in the properties. It uses the HTTP range header to partly retrieve contents of the log file. Yes
prometheus This endpoint shows metrics in a format that can be scraped by a Prometheus server. Yes

Enabling endpoints

使用 Spring Boot Actuator,所有端点默认启用,除了 shutdown 端点。为了启用或禁用特定端点,应在 application.properties 文件中添加相关属性。以下是启用端点的格式:

management.endpoint.<id>.enabled=true

例如,可以添加以下属性来启用 shutdown 端点:

management.endpoint.shutdown.enabled=true

当我们在启用默认执行器端点的情况下引导应用程序时,可以看到以下日志条目:

2018-03-24 17:51:36.687 INFO 8516 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2018-03-24 17:51:36.696 INFO 8516 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2018-03-24 17:51:36.697 INFO 8516 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto protected java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springframework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)

仔细查看日志条目,我们发现暴露了以下端点或 URL:

  • /actuator
  • /actuator/health
  • /actuator/info

为什么应用程序在前面列出的这么多端点中暴露了三个端点?为了回答这个问题,Spring Boot Actuator 仅通过 HTTP 公开了三个端点。前面列出的其余端点通过 JMX 连接公开。以下是端点列表以及有关它们是通过 HTTP 还是 JMX 公开的信息:

ID Exposed over JMX Exposed over HTTP
auditevents Yes No
beans Yes No
conditions Yes No
configprops Yes No
env Yes No
flyway Yes No
health Yes Yes
heapdump N/A No
httptrace Yes No
info Yes Yes
jolokia N/A No
logfile N/A No
loggers Yes No
liquibase Yes No
metrics Yes No
mappings Yes No
prometheus N/A No
scheduledtasks Yes No
sessions Yes No
shutdown Yes No
threaddump Yes No

为什么 Spring Boot 默认不通过 HTTP 公开所有端点?原因是端点可能会暴露敏感信息。因此,在暴露它们时应该仔细考虑。

以下属性可用于更改或覆盖端点的默认暴露行为:

  • management.endpoints.jmx.exposure.exclude: The endpoint IDs specified in a comma-separated list are excluded from default exposure over the JMX connection. By default, none of the default endpoints are excluded.
  • management.endpoints.jmx.exposure.include: The endpoint IDs specified in a comma-separated list are included along with the default exposure over the JMX connection. The property can be used to expose those endpoints that are not included in the default list of endpoints. The default value for the property is *, which indicates that all of the endpoints are exposed.
  • management.endpoints.web.exposure.exclude: The endpoint IDs specified by a comma-separated list are excluded from being exposed over HTTP. Though no default value exists, only info and health endpoints are exposed. The rest of the endpoints are implicitly excluded for HTTP.
  • management.endpoints.web.exposure.include: The endpoint IDs specified in a comma-separated list are included along with the default exposure over HTTP. The property can be used to expose those endpoints that are not included in the default list of endpoints. The default value for the property is info, health.

Health checks

确保应用程序高性能的极其关键的方面之一是监控应用程序的运行状况。生产级应用程序始终处于专门的监控和警报软件的监视之下。为每个参数配置阈值,无论是平均响应时间、磁盘利用率还是 CPU 利用率。一旦参数值超过指定的阈值,监控软件就会通过电子邮件或通知发出警报。开发和运营团队采取必要措施确保应用程序恢复正常状态。

对于 Spring Boot 应用程序,我们可以通过导航到 /actuator/health URL 来收集健康信息。 health 端点默认启用。对于部署在生产环境中的应用程序,使用 health 端点收集的健康信息可以发送到监控软件以用于警报目的。

health 端点提供的信息取决于 management.endpoint.health.show-details 属性。以下是该属性支持的值列表:

  • always: It indicates that all the information should be shown to all users.
  • never: It indicates that the details should never be shown.
  • when-authorized: This indicates that the details are shown to users with authorized roles only. The authorized roles can be configured using the management.endpoint.health.roles property.

show-details 属性的默认值为 never。此外,当用户具有一个或多个端点的授权角色时,可以认为该用户已授权。默认情况下,没有一个角色被配置为授权。因此,所有经过身份验证的用户都被视为授权用户。

HealthIndicator 是重要的接口之一,它在磁盘空间、数据源或 JMS 等不同方面提供应用程序健康状况的指示。 health 端点从应用程序上下文中定义的所有 HealthIndicator 实现 bean 收集健康信息。 Spring Boot 带有一组自动配置的健康指标。该框架足够灵活,可以支持自定义健康指标实现。应用程序的最终健康状态由 HealthAggregator 派生。健康聚合器根据已定义的状态顺序对所有健康指标中的状态进行排序。

以下是 Spring Boot 自动配置的 HealthIndicators 列表:

  • CassandraHealthIndicator: Checks whether the Cassandra database is up
  • DiskSpaceHealthIndicator: Checks whether enough disk space is available
  • DataSourceHealthIndicator: Checks whether the connection with the data source can be obtained or not
  • ElasticSearchHealthIndicator: Checks whether the elasticsearch cluster is up
  • InfluxDbHealthIndicator: Checks whether the Influx server is up and running
  • JmsHealthIndicator: Checks whether the JMS broker is up and running
  • MailHealthIndicator: Checks whether the mail server is up and running
  • MongoHealthIndicator: Checks whether the Mongo database is up and running
  • Neo4jHealthIndicator: Checks whether the Neo4j server is up and running
  • RabbitHealthIndicator: Checks whether the Rabbit server is up and running
  • RedisHealthIndicator: Checks whether the Redis server is up and running
  • SolrHealthIndicator: Checks whether the Solr server is up and running

这些健康指标是根据适当的 Spring Boot 启动器配置自动配置的。

以下是我们导航到 http://localhost:8080/actuator/health URL 时的示例磁盘空间运行状况检查输出:

{
  "status": "UP",
  "details": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 407250137088,
        "free": 392089661440,
        "threshold": 10485760
      }
    }
  }
}

我们可以添加额外的自定义健康指标来包含我们想要查看的信息。自定义的健康指标将显示在 health 端点的结果中。创建和注册自定义健康指标非常容易。

以下是自定义运行状况指示器的示例:

package com.packt.springhighperformance.ch09.healthindicators;

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;

@Component
public class ExampleHealthCheck extends AbstractHealthIndicator {
    @Override
      protected void doHealthCheck(Health.Builder builder) 
      throws Exception   
   {
        // TODO implement some check
        boolean running = true;
        if (running) {
          builder.up();
        } else {
          builder.down();
        }
    }
}

我们必须创建一个继承自 AbstractHealthIndicator 的 Java 类。在自定义健康指标类中,我们必须实现 doHealthCheck() 方法。该方法需要传递一个 Health.Builder 对象。如果我们发现健康参数OK,那么应该调用builder.up()方法,否则应该调用builder.down()方法。

以下是点击 /actuator/health URL 时在页面上呈现的输出:

{
  "status": "UP",
  "details": {
    "exampleHealthCheck": {
      "status": "UP"
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 407250137088,
        "free": 392071581696,
        "threshold": 10485760
      }
    },
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "hello": 1
      }
    }
  }
}

自定义健康指标不需要注册。 @Component 注释被扫描,bean 被注册到 ApplicationContext

到目前为止,我们已经通过示例详细了解了 Spring Boot。以下部分将重点介绍 Spring Boot 与微服务的使用。

Microservices using Spring Boot

我们现在从前面的部分中获得了大量关于 Spring Boot 的信息。有了目前所掌握的信息,我们现在可以使用 Spring Boot 构建微服务。在开始使用 Spring Boot 实现我们的第一个微服务之前,为了继续实现第一个微服务,假设您了解微服务的基本信息,包括单体应用的问题、微服务是什么以及微服务带来的特性.

First microservice with Spring Boot

以下是我们将要开发的微服务的详细信息:

  • We will implement an accounting service as a microservice.
  • The microservice will be REST-based. It is an architectural pattern for developing web services. It focuses on identifying each resource in the application with a unique URL.
  • We will identify the Spring Boot starter project that we will need and generate the Maven pom.xml file accordingly.
  • We will implement an Account class with a few basic properties.
  • We will implement AccountRepository with the find-by-name example method.
  • We will implement the controller class, which has a repository auto-wired. The controller exposes the endpoints.
  • We will also implement a way to feed the test data into the database.

开始吧!

我们将通过使用 Spring Initializr 生成 Spring Boot 应用程序来开始实现。我们必须决定要使用的 Spring Boot 启动项目。我们想开发一个基于 JPA 的 Web 应用程序。为了在数据库中存储 Account 数据,我们可以使用 MySQL 或 H2。通常,H2 是一个更方便的选项,因为我们不需要设置任何东西。我们将在本章的示例中使用 MySQL。

以下是要选择的启动项目:

  • Web
  • JPA
  • MySQL or H2
  • REST repositories

我们还可以添加 Spring Boot Actuator 用于应用程序监控,但对于示例来说不是强制性的。

以下是 Spring Initializr 生成的 pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.packt.springhighperformance.ch09</groupId>
  <artifactId>ch-09-accounting-service</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>accounting-service</name>
  <description>Example accounting service</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-    
    8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-rest-hal-browser</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Spring Initializr 生成的另一段代码是 Spring Boot 应用程序:

package com.packt.springhighperformance.ch09.accountingservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccountingServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(AccountingServiceApplication.class, args);
  }
}

此时,我们应该将我们的项目导入到我们偏好的 IDE 中。

各位,现在就为动手开发做好准备吧。我们将从创建 Account JPA 实体类开始。我们将使用 @Entity@Table 注释来注释 Account 类。 @Table 注释允许我们提供所需的表名。我们还有一列,即 accountName。它存储并表示 Account 的名称。基本上,Account 实体代表现实世界中的帐户类型。我们添加的另一个重要属性是 idid 表示一个唯一的、自动生成的数字标识符。我们可以使用标识符唯一地识别每个帐户。 @GeneratedValue 注释让我们提供在数据库中生成 id 值的方式。将其保持为 AUTO 定义它依赖于数据库来自动生成 id 值。 @Column 注释允许我们将 accountName 属性与 ACCT_NAME 数据库字段相匹配。

以下是 Account 实体的代码:

package com.packt.springhighperformance.ch09.accountingservice.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "accounts")
public class Account {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "ACCT_ID")
  private Long id;

  @Column(name = "ACCT_NAME")
      private String accountName;

  public Account() {
  }

  public Account(String accountName) {
    this.accountName = accountName;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getAccountName() {
    return accountName;
  }

  public void setAccountName(String accountName) {
    this.accountName = accountName;
  }
  
  @Override
  public String toString() {
    return "Account{"
        + "id=" + id + 
        ", accountName='" + accountName + '\'' +
        '}';
  }

}

Spring Data 提供了一个方便的接口来执行常见的数据库操作。该接口称为CrudRepository。它支持特定类型的基本 CreateReadUpdateDelete 操作。该接口由 JpaRepository 接口继承,该接口是 CrudRepository 接口的 JPA 特定定义。 JpaRepository 还从 PagingAndSortingRepository 接口继承排序和分页功能。

有了这个背景,我们接下来的任务就是构建一个与accounts数据库表交互的接口。以下是 AccountsRepository 类的代码:

package com.packt.springhighperformance.ch09.
accountingservice.repositories;

import java.util.Collection;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import com.packt.springhighperformance.ch09.accountingservice.models.Account;

@RepositoryRestResource
public interface AccountsRepository extends JpaRepository<Account, Long> {

  Collection<Account> findByAccountName(@Param("an") String an);
}

AccountsRepository 接口中,我们定义了一个方法,该方法旨在根据 accountName 从数据库中查找 Account 条目。 CrudRepository 接口非常强大。它将生成 findByAccountName 方法的实现。它可以为所有此类遵循约定的查询方法生成实现,例如 findBy{model-attribute-name}。它还返回 Account 类型的对象。

此外,您可能已经注意到 @RepositoryRestResource 的使用是由 Spring Data REST 模块提供的。它简要地将用于数据操作的存储库方法公开为 REST 端点,而无需任何进一步的配置或开发。

现在,我们已经有了实体和存储库。接下来是 Web 应用程序的控制器部分。我们必须创建一个控制器类。以下是 AccountsController 类的代码:

package com.packt.springhighperformance.ch09
.accountingservice.controllers;

import java.util.Collections;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AccountsController {
  @GetMapping(value = "/account/{name}")
  Map<String, Object> getAccount(@PathVariable String name) {
    return Collections.singletonMap("Account : ", name);
  }
}

AccountsController 代码中的三个值得注意的注释是:

  • @RestController: This annotation is a combination of the @Controller and @ResponseBody annotations. If we used the @RestController annotation, we don't need to define these two other annotations. The @RestController annotation indicates that the class should be treated as a controller and every endpoint method will respond with content as a response body.
  • @GetMapping: This annotation is used to define a REST GET endpoint mapping.
  • @PathVariable: This annotation is used to fetch the values supplied in the URL path itself.

剩下两件事。一个是数据库和其他重要属性,而另一个是在 accounts 表中填充初始数据的一种方式。

以下是管理应用程序配置部分的 application.properties 文件:

spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:mysql://localhost:3306/db_example?useSSL=false
spring.datasource.username=root
spring.datasource.password=root

从属性列表中,spring.jpa.hibernate.ddl-auto 属性根据提供的数据库配置确定数据库的初始生成。它确定 Spring Boot 应用程序是否应该在应用程序启动时创建数据库模式。 nonevalidateupdatecreatecreate-drop 是选项可用于物业。

在启动应用程序时,我们还可能收到以下错误:

Establishing SSL connection without server's identity verification is not recommended.

我们可以在数据库连接 URL 中使用 useSSL=true 来克服此警告,如您在前面代码示例中的属性中所见。

Loading sample data into the database

此时,需要在数据库的accounts表中有一些初始数据。它将帮助我们测试我们开发的帐户的微服务。 Spring 模块提供了多种方法来实现这一点。

The JPA way of initial data loading

Spring Data JPA 提供了一种在应用程序启动时执行数据库操作命令的方法。由于模式将从 JPA 实体配置和 ddl-auto 属性值在数据库中生成,因此我们必须注意仅在 accounts 表中插入帐户记录.以下是完成此操作的步骤:

  1. Add the following property to the application.properties file:
spring.datasource.initialization-mode=always
  1. Create a data.sql file with INSERT queries in the src/main/resources folder of the project:
INSERT INTO accounts (ACCT_NAME) VALUES
  ('Savings'),
  ('Current'),
  ('Fixed Deposit'),
  ('Recurring Deposit'),
  ('Loan');

而已!当我们启动应用程序时,Spring会自动将数据插入到数据库中的accounts表中。

The ApplicationRunner way of initial data loading

我们也可以使用 ApplicationRunner 接口来完成此操作。该接口负责在应用程序启动时执行 run 方法中定义的代码。

以下是 ApplicationRunner 接口的实现代码:

package com.packt.springhighperformance.ch09.accountingservice;

import java.util.stream.Stream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import com.packt.springhighperformance.ch09.accountingservice.models.Account;
import com.packt.springhighperformance.ch09.accountingservice.repositories.AccountsRepository;

@Component
public class AccountsDataRunner implements ApplicationRunner {

  @Autowired
  private AccountsRepository acctRepository;

  @Override
  public void run(ApplicationArguments args) throws Exception {
    Stream.of("Savings", "Current", "Recurring", "Fixed Deposit")
    .forEach(name -> acctRepository.save(new Account(name)));
    acctRepository.findAll().forEach(System.out::println);
  }

}

我们已经自动连接存储库,以便我们可以访问 AccountsRepository 方法来将 accounts 记录插入数据库。

Microservice client

现在我们已经有了微服务,我们必须看看如何使用它。计划是使用 Spring Initializr 创建另一个 Web 应用程序,并使用适当的工具来使用会计微服务。

以下是客户端应用程序的 POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.packt.springhighperformance.ch09</groupId>
  <artifactId>ch-09-accounting-service-client</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>accounting-service-client</name>
  <description>Example accounting service client</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-
    8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.M9</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

</project>

在前面的 pom.xml 文件中,我们使用 Maven 依赖项管理元素导入了 Spring Cloud 依赖项。我们还添加了 openfeign 启动项目。 Feign 是一个使用 Web 服务的客户端工具,并提供了一个 REST 客户端模板工具。

以下是我们 Spring Boot 客户端应用程序中 main 类的代码:

package com.packt.springhighperformance.ch09.accountingclient;

import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.json.BasicJsonParser;
import org.springframework.boot.json.JsonParser;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class AccountingServiceClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(AccountingServiceClientApplication.class, 
    args);
  }
}

@RestController
class MainController {

  @Value("${accounting.service.url}")
  private String accountingServiceUrl;

  @GetMapping("/account")
  public String getAccountName(@RequestParam("id") Long id) {
    ResponseEntity<String> responseEntity = new 
    RestTemplate().getForEntity(accountingServiceUrl + "/" + id,
    String.class);
    JsonParser parser = new BasicJsonParser();
    Map<String, Object> responseMap = 
    parser.parseMap(responseEntity.getBody());
    return (String) responseMap.get("accountName");
  }
}

我们在同一个 Java 文件中定义了 REST 控制器。

以下是定义微服务 URL 并定义运行客户端应用程序的 server.portapplication.properties 文件:

accounting.service.url=http://localhost:8080/accounts/
server.port=8181

Microservices with Spring Cloud

Spring Cloud 提供了一种声明式方法来构建云原生 Web 应用程序。云原生是一种应用程序开发范式,旨在鼓励采用价值驱动的开发最佳实践。 Spring Cloud 构建在 Spring Boot 之上。 Spring Cloud 为分布式系统中的所有组件提供了一种轻松访问所有功能的方法。

Spring Cloud 提供:

  • Git-managed versioning of centralized configuration data
  • Pairing with Netflix Eureka and Ribbon for application services to discover each other dynamically
  • Pushing away load-balancing decisions from a dedicated proxy load balancer to client services

外部化配置是 Spring Cloud 的主要优势之一。在下一节中,我们将开发一个示例来展示 Spring Boot 应用程序的外部化配置。

Spring microservice configuration example

为了使外部化配置工作,我们需要建立一个集中的配置服务器。配置服务器将为注册的 Spring Boot 应用程序存储和提供配置数据。在本节中,我们将开发一个配置服务器,我们之前开发的会计服务将作为配置客户端。

以下是 Spring Boot 配置服务器的 POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.spring.server.config</groupId>
  <artifactId>spring-config-server</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>config-server</name>
  <description>Example spring boot config server</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-
    8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.M9</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-server</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
</project>

前面的依赖需要注意两个配置:

  • spring-cloud-dependencies: It provides a set of dependencies necessary for Spring Cloud projects
  • spring-cloud-config-server: This is the Spring Cloud starter project for Spring Boot

以下是 application.properties 文件:

spring.application.name=configserver
spring.cloud.config.server.git.uri:${user.home}\\Desktop\\config-repo
server.port=9000
spring.profiles.active=development,production

spring.cloud.config.server.git.uri 属性指向存储配置的基于 Git 的目录。版本控制由 Git 自己维护。

spring.profiles.active 表示应用程序要使用的配置文件。对于开发团队来说,拥有多个环境是一个常见的用例。为了对每个环境进行单独的配置,我们可以使用这个属性。

@EnableConfigServer 注解由 Spring Cloud 入门项目提供。它将类标记为配置服务器。以下是 Spring Boot 应用程序 main 类的代码:

package com.spring.server.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ConfigServerApplication.class, args);
  }
}

一旦完成,配置服务器就可以运行了。在 Git 存储库中,我们创建了一个 accountingservice.properties 文件,其内容如下:

server.port=8101

启动应用程序后,我们可以导航到 http://localhost:9000/accountingservice/default。由于我们在配置服务器中没有 accountingservice 应用程序的配置文件特定文件,因此它会选择默认配置文件。页面内容如下所示:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

正如我们所见,server.port 属性值在页面上呈现。

下一步是构建一个使用配置服务器中定义的集中配置的客户端。我们必须创建一个具有 Web 依赖关系的 Spring Boot 启动应用程序。

以下是配置服务器客户端的 POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.packt.springhighperformance.ch09</groupId>
  <artifactId>ch-09-accounting-service</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>accounting-service</name>
  <description>Example accounting service</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-
    8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-config</artifactId>
      <version>2.0.0.M9</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

正如我们在前面的 Maven 文件中看到的,我们需要添加 spring-cloud-config-starter 项目作为依赖项。该项目为要注册为配置服务器客户端的应用程序提供了必要的配置。

以下是 application.properties 文件:

management.endpoints.web.exposure.include=*
server.port=8888

为了将应用程序注册为配置服务器的客户端,我们必须启用管理 Web 端点。根据 application.properties 文件中的配置,服务器将在端口 8888 上运行。

Spring Cloud 在一个额外的上下文上运行,称为 bootstrap 上下文。引导上下文是主 ApplicationContext 的父级。引导上下文的职责是将配置属性从外部源加载到本地外部配置中。建议为引导上下文创建一个单独的属性文件。

以下是 bootstrap.properties 文件中的属性:

spring.application.name=accountingservice
spring.cloud.config.uri=http://localhost:9000

我们已经定义了与存储在配置服务器上的 Git 目录中的配置属性文件的名称相匹配的应用程序名称。 bootstrap.properties 文件还定义了 Spring Cloud 配置服务器的 URL。

这就是客户端向 Spring Cloud 配置服务器注册的全部内容。服务器启动时可以看到以下日志条目:

2018-04-01 16:11:11.196 INFO 13556 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:9000
....

2018-04-01 16:11:13.303  INFO 13556 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8101 (http)
....

2018-04-01 16:11:17.825  INFO 13556 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8101 (http) with context path ''

如您所见,虽然我们已将客户端应用程序的服务器端口定义为 8888,但它会从配置服务器获取 server.port 属性并在端口上启动 Tomcat 8101。以下是我们渲染 /accounts URL 时页面的样子:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

本节描述了创建简单配置服务器和使用配置服务器的客户端的分步方法。在接下来的部分中,我们将看到一种监控 Spring 微服务的方法。

Monitoring microservices with Spring Boot admin

Spring Boot admin 是一个便于监控和管理 Spring Boot 应用程序的应用程序。最新版本的 Spring Boot 管理应用程序尚不兼容 Spring 2.0.0。出于本节展示的示例的目的,我们使用了 Spring Boot 1.5.11 快照。 Spring Boot 管理版本是 1.5.4。

Spring Boot 客户端应用程序通过 HTTP 向 Spring Boot 管理应用程序注册自己。管理员应用程序也有可能使用 Spring Cloud Eureka 发现服务发现客户端应用程序。 Spring Boot 管理用户界面是在 AngularJS 中通过 Actuator 端点构建的。

这对于介绍部分来说应该足够了,因为示例将提供更多的洞察力。让我们首先构建 Spring Boot 管理服务器。

spring-boot-admin-server 是构建管理服务器应用程序的依赖项。 Spring Boot 管理应用程序可以注册多个 Spring Boot 应用程序,因此,Spring Boot 管理应用程序必须是安全的。这就是我们添加 Spring Security 启动项目依赖的原因。我们将为此应用程序的目的加入基本身份验证,但这不是限制。我们可以添加高级安全机制,例如 OAuth,以保护应用程序。以下是 Spring Boot 管理服务器的 POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.spring.admin</groupId>
  <artifactId>admin-server</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>admin-server</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.11.BUILD-SNAPSHOT</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-server</artifactId>
      <version>1.5.4</version>
    </dependency>
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-server-ui</artifactId>
      <version>1.5.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-server-ui-login</artifactId>
      <version>1.5.4</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
</project>

application.properties 文件是我们定义访问管理应用程序的安全凭证的地方。以下是 application.properties 文件的内容:

security.user.name=admin
security.user.password=admin

@EnableAdminServer 由 Spring Boot 管理服务器依赖项提供。它表明该应用程序作为 Spring Boot 管理应用程序工作。以下是 Spring Boot 应用程序 main 类的代码:

package com.spring.admin.adminserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import de.codecentric.boot.admin.config.EnableAdminServer;

@SpringBootApplication
@EnableAdminServer
public class AdminServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(AdminServerApplication.class, args);
  }
}

下一步是构建一个将在 Spring Boot 管理应用程序中注册的示例应用程序。以下是 POM 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.11.BUILD-SNAPSHOT</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <spring-boot-admin.version>1.5.7</spring-boot-admin.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</project>

我们必须定义以下属性:

  • spring.boot.admin.url: The URL points to the Spring Boot admin application.
  • spring.boot.admin.username: It is necessary for the admin client to access the admin application using security credentials. This property specifies the username for the admin application.
  • spring.boot.admin.password: This property specifies the password for the admin application.
  • management.security.enabled: This property denotes whether security is enabled for the client application or not.
  • security.user.name: This property defines the username for accessing the client application.
  • security.user.password: This property specifies the password for accessing the client application.

以下是 application.properties 文件:

spring.boot.admin.url=http://localhost:8080
server.port=8181
spring.boot.admin.username=admin
spring.boot.admin.password=admin
management.endpoints.web.exposure.include=*
security.user.name=user
security.user.password=password
management.security.enabled=false

以下是简单的 Spring Boot 应用程序类的代码:

package com.spring.admin.client.bootadminclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BootAdminClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(BootAdminClientApplication.class, args);
  }
}

还可以向 Spring Security 提供的默认 Web 安全配置添加自定义。以下是演示允许所有授权请求的示例:

package com.spring.admin.client.bootadminclient;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().permitAll().
    and().csrf().disable();
  }
}

此时,我们已准备好启动 Spring Boot 管理和客户端应用程序。当我们导航到 Spring Boot 管理应用程序 URL 时,将显示以下屏幕,其中包含所有已注册应用程序的列表:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

单击应用程序名称右侧的 Details 按钮将弹出一个类似于此处显示的界面。 Details 选项卡显示应用程序的健康状况、内存和 JVM 统计信息以及垃圾收集器详细信息:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

Logging 选项卡查看应用程序详细信息并显示所有已配置记录器的列表。可以更改日志级别。以下是Logging的接口:

读书笔记《hands-on-high-performance-with-spring-5》Spring Boot微服务性能调优

这就是 Spring Boot 管理应用程序的全部内容。它提供了用于监控 Spring Boot 应用程序的生产级接口和详细信息。下一节提供 Spring Boot 应用程序的性能调优。

Spring Boot performance tuning

Spring Boot 是一个很好的工具,可以非常快速地引导和开发基于 Spring Framework 的应用程序。毫无疑问,Spring Boot 应用程序的 vanilla 版本提供了高性能。但随着应用程序开始增长,其性能开始成为瓶颈。这是所有 Web 应用程序的正常情况。当添加不同的功能并且传入的请求日益增加时,就会观察到性能下降。本节我们将学习 Spring Boot 应用程序的性能优化技术。

Undertow as an embedded server

Spring Boot 提供了可以在 JAR 文件中运行 Web 应用程序的嵌入式服务器。一些可用的嵌入式服务器是 Tomcat、Undertow、Webflux 和 Jetty。建议将 Undertow 作为嵌入式服务器。与 Tomcat 和 Jetty 相比,Undertow 提供更高的吞吐量并消耗更少的内存。以下比较可能会提供一些见解:

  • Throughput comparison:

服务器

样本

错误%

吞吐量

雄猫

3000

0

293.86

码头

3000

0

291.52

暗流

3000

0

295.68

  • Heap memory comparison:

服务器

堆大小

二手

最大

雄猫

665.5 MB

118.50 MB

2 GB

码头

599.5 MB

297 MB

2GB

暗流

602 MB

109 MB

2GB

  • Threads comparison:

服务器

直播

开始

雄猫

17

22

码头

19

22

暗流

17

20

从前面的比较来看,Undertow 看起来是 Spring Boot 应用程序中嵌入式服务器的明显选择。

Overhead with the @SpringBootApplication annotation

@SpringBootApplication 注释是为过去使用 @ComponentScan@EnableAutoConfiguration@Configuration 注释 Spring 类的开发人员提供的。所以,@SpringBootApplication注解等价于使用默认配置的三个注解。隐式 @ComponentScan 注解会扫描基包(Spring Boot 应用程序主类的包)和所有子包中定义的 Java 类。当应用程序的大小显着增长时,这会减慢应用程序的启动速度。

为了克服这个问题,我们可以将 @SpringBootApplication 注释替换为单独的注释,其中我们提供要使用 @ComponentScan 扫描的包路径。我们还可以考虑使用 @Import 注释来仅导入所需的组件、bean 或配置。

Summary

本章从有关 Spring Boot、Spring Cloud、微服务以及所有这些的深刻细节开始。我们介绍了 Spring Initializr、Spring Boot 启动项目的详细信息,并学习了如何创建我们的第一个 Spring Boot 应用程序。然后,我们了解了 Spring Boot Actuator 以及 Actuator 提供的生产级特性。应用程序健康检查和端点的详细信息对于生产就绪的应用程序很重要。

在本章的后面,我们迁移到了微服务的世界。我们了解了 Spring Boot 如何利用特性来构建微服务。我们使用 Spring Boot 和 Spring Cloud 开发了一个支持外部化配置的微服务。我们还查看了用于监控 Spring Boot 应用程序的 Spring Boot 管理员的集成。最后但同样重要的是,我们学习了一些提高 Spring Boot 应用程序性能的技术。相当大的东西,不是吗?

至此,您对 Spring 的性能评估和性能调优有了很好的了解,本质上,任何基于 Java 的 Web 应用程序都可以使用。这就是本书的范围。更进一步,您可以学习 JVM 类加载机制、Spring Batch 框架、微服务设计模式、微服务部署和基础架构即服务(IaaS)。我们希望这些对您有所帮助。