《Spring + Cloud微服务实战》读书笔记(二)————Spring Boot

时间:2022-05-24 17:26:55

1.Spring Boot简介

1.1简化配置

通过设计大量的自动化配置等方式来简化Spring原有样板化的配置,使得开发者可以快速构建应用。

1.2简化依赖管理

Spring Boot 通过一系列Starter POMs的定义,让我们整合各项功能的时候,不需要再Maven的pom.xml中
维护那些错中复杂的依赖关系,而是通过类似模块化的Starter模块定义来引用,使得依赖管理工作变得更为简单。

1.3简化部署

在如今容器化大行其道的时代,Spring Boot除了可以很好融入Docker之外,其自身就支持嵌入式大的Tomcat、Jetty等容器。
所以,通过 Spring Boot 构建的应用不再需要安装Tomcat,只需要将Spring Boot应用打成jar包,并通过
java -jar 命令直接运行就能启动一个标准化的Web应用,这使得Spring Boot应用变得非常轻便。

1.4优化开发环节

整个Spring Boot的生态系统都使用到了Groovy,可以通过使用Gradle和Groovy来开发Spring Boot应用。

@RestController
public class HelloController {

@RequestMapping("/hello")
public String index(){
    return "Hello World";
}

}
这段代码编译打包后,使用 java -jar 命令就能启动一个烦恼会“hello World”的RESTful API。

2.快速入门

2.1构建Maven项目

访问 http://start.spring.io/ ,该页面提供了以Maven 或 Gradle构建Spring Boot 项目的功能。
《Spring + Cloud微服务实战》读书笔记(二)————Spring Boot

将下载的压缩包,解压,导入到Eclipse.

2.2目录介绍

src/main/java: 代码目录
src/main/resources:配置目录。
该目录用来存放应用的一些配置信息,比如应用名,服务端口,数据库链接等。
该目录还有 static目录和templates目录。
static : 存放静态资源,如图片、CSS、JavaScript等。
templates:存放Web页面的模板文件
src/test/:单元测试目录。
通过JUnit 4实现测试类,可以直接用运行Spring Boot应用的测试。

2.3 Maven配置

<?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.study</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.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</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>
    </dependencies>

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

父项目中定义了Spring Boot版本的基础依赖以及一些默认配置内容,比如,配置文件 application.properties的位置等。

项目依赖dependencies配置中,包含了:

  • spring-boot-starter-web: 全栈Web开发模块,包含嵌入式Tomcat、Spring MVC.

  • spring-boot-starter-test:通用测试模块,包含JUnit,Hamcrest,Mockito.

    这里引入的web和test模块,在Spring Boot生态中被称为Starter POMs.
    Starter POMs是一系列轻便的依赖包,是一套一站式的Spring相关技术的解决方案。

    开发Web应用的时候,引入spring-boot-starter-web。
    需要访问数据库能力时,再引入spring-boot-starter-jdbc 或spring-boot-starter-data-jpa.

在使用Spring Boot构建应用的时候,各项功能木块的整合不再需要在pom.xml中做的大量的依赖配置,
而是通过使用 Starter POMs定义的依赖包,使得模块整合变得非常轻巧,易于理解与使用。

2.3 构建和启动

项目构建的build部分,引入了Spring Boot的Maven插件,该插件可以帮助我们方便地启停应用,
这样在开发时就不用每次去找主类或是打包成jar来运行微服务。
只需要通过 mvn spring-boot:run 命令就可以快速启动Spring Boot应用。

在服务器上部署运行时,通常先使用 mvn install 将应用打包成jar包,在通过 java -jar xxx.jar来启动应用

2.4编写单元测试

功能实现之后,要随手写配套单元测试的习惯,这在微服务架构中尤为重要。
在src/test/下写测试类:

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes=HelloController.class)
    @WebAppConfiguration
    public class HelloApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp()  throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
    }

    @Test
    public void hello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(content().string(equalTo("Hello World")));
    }
}

代码解析:

  • @RunWith(SpringJUnit4ClassRunner.class) 引入Spring 对 JUnit4 的支持
  • @SpringBootTest(classes=HelloController.class) 指定Spring Boot的启动类
  • @WebAppConfiguration 开启Web应用的配置,用于模拟ServletContext
  • MockMvc 对象:
    用于模拟调用Controller的接口发起请求。
    • perform函数执行一次请求调用
    • accept用于执行接收的数据类型
    • andExpect用于判断接口返回的期望值
  • @Before 初始化对HelloController的模拟
    静态引入,让status、content、equalTo函数可用:
    import static org.hamcrest.Matchers.equalTo;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

2.5配置文件

Spring Boot 的默认配置文件位置为src/main/resources/application.properties
关于Spring Boot应用的配置内容都可以集中在该文件中,根据我们引入的不同Starter模块,
可以在这里定义容器端口号、数据库连接信息、日志级别等各种配置信息。
如:

  • 自定义Web模块的服务端口号:
    server.port=8888
  • 指定应用名:
    spring.application.name=hello

Spring Boot的配置文件除了可以使用传统的properties文件之外,还支持YAML文件。
YAML采用的配置格式是以类似大纲的缩进形式来表示:

environments:
        dev:
            url: http://dev.bar.com
            name: Developer Setup
        prod:
            url: http://foo.bar.com
            name: My Cool App

通过YAML的配置方式,利用阶梯化缩进方式,其结构更为清晰易读,同时配置内容的字符量也显著减少。
另外,YAML还可以在一个单个文件中通过使用spring.profiles属性来定义多个额不用的环境配置。

server:
 port:8881 ---
spring:
 profiles:test
server:
 port:8882 ---
spring:
 profiles:prod
server:
 port:8883

通过上面的配置,如果指定为test环境时,server.port将使用8882端口;
而在prod环境中,server.port将使用8883端口;
如果没有指定环境,server.port将使用8881端口。

注意

  1. YAML目前无法通过@PropertySource注解来加载配置
  2. YAML将属性加载到内存中保存的时候是有序的,所以当配置文件中的信息需要具体顺序含义时,应选择YAML

    2.5.1自定义参数

    可以在配置文件中定义一些我们需要的自定义属性。

book.name=SpringBook
book.author=spring

在应用中可以通过@Value注解来加载这些自定义参数:

@Component
public class Book {
    @Value("${book.name}")
    private String name;
    @Value("${book.author}")
    private String author;
}

@Value注解加载属性值的时候可以支持两种表达式:

  • 一种是上面的PlaceHolder方式,格式为${…},大括号内为PlaceHolder
  • 另一种是使用SpEL表达式(Spring Expression Language),格式为#{…},大括号内为SpEL表达式

2.5.2参数引用

在application.properties中国的各个参数之间可以直接通过使用PlaceHolder的方式来进行引用:

book.name=SpringBook
book.author=spring
book.desc=${book.author} is writing 《${book.name}》

2.5.3使用随机数

在一些特殊情况下,需要有些参数每次被加载的时候不是一个固定的值,如密钥、服务端口等。
在Spring Boot的属性配置文件中,可以通过使用${random}配置来产生随机的int值、long值或者String字符串。

${random}的配置方式使用:

# 随机字符串
com.randomString.value=${random.value}
# 随机 int
com.randomInt.value=${random.int}
# 随机 long
com.randomLong.value=${random.long}
#10 以内的随机数
com.randomInt.test1=${random.int(10)}
#10~20 的随机数
com.randomInt.test2=${random.int[10,20]}

2.5.4命令行参数

Spring Boot应用,可以使用命令 java -jar 来启动的方式,该命令还可以指定应用参数,如:
java -jar xxx.jar –server.port=8888

在用命令行方式启动Spring Boot 应用时,使用“–”符号,就是对application.properties中的属性值进行赋值的标识。

通过命令行来修改属性值是 Spring Boot 非常重要的一个特性。
通过此特性,理论上已经使得应用的属性在启动前是可变得,所以端口号、数据库连接等,都是可以在应用启动时发生改变的。

2.5.5多环境配置

对于多环境的配置,各种项目构建工具或是框架的基本思路是一致的,通过配置多份不同环境的配置文件,
再通过打包命令指定需要打包的内容之后进行区分打包。

在Spring Boot中,多环境配置的文件名需要满足 application-{profile}.properties的格式,其中
{profile}对应环境标识,如:

  • application-dev.properties 开发环境
  • application-test.properties 测试环境
  • application-prod.properties 生产环境

至于具体哪个配置文件会被加载,需要在 application.properties 文件中通过
spring.profiles.active 属性来设置,其值对应配置文件中的{profile}值。

如spring.profiles.active=test就会加载 application-test.properties配置文件内容。

2.5.6加载顺序

不同环境配置的修改不得不去获取工程内容来修改这些配置内容,当应用非常多的时候就会变得非常不方便。
同时,配置内容对开发人员都可见,这本身也是一种安全隐患,对此,出现了很多将配置内容外部化的框架和工具。
为了能够更合理地重写各属性的值,Spring Boot 使用了下面的属性加载顺序:

  1. 在命令行中能够传入的参数
  2. SPRING_APPLICATION_JSON 中的属性。SPRING_APPLICATION_JSON 是以JSON格式配置在系统环境变量中的内容
  3. java:comp/env 中的JNDI属性
  4. Java 的系统属性,可以通过System.getProperties()获得的内容
  5. 操作系统的环境变量
  6. 通过random.*配置的随记属性
  7. 位于当前应用ja包之外,针对不同{profile} 家门口的配置文件内容,例如application-{profile}.properties或是YAML定义的配置文件。
  8. 位于当前应用ja包之内,针对不同{profile} 家门口的配置文件内容,例如application-{profile}.properties或是YAML定义的配置文件。
  9. 位于当前jar包之外的application.properties和YAML配置内容
  10. 位于当前jar包之内的application.properties和YAML配置内容
  11. 在@Configuration注解修改的类中,通过@PropertySource注解定义的属性
  12. 应用默认属性,使用SpringApplication.setDefaultProperties定义的内容

数字越小优先级越高。

可以看到,其中第7项和第9项都是从应用jar包之外读取配置文件,所以,实现外部化
配置低的原理就是从此切入,为其制定外部配置文件的加载位置来取代jar包之内的配置内容。

2.5.6监控与管理

由于部署应用的数量成倍增长,系统及全中出现故障低的频率也变得越来越高,虽然在高可用机制的保护
下,个别故障不会影响系统的对外服务,但是这些频繁出现的故障需要做到高效运维,需要实现一套自动
化的监控运维机制。

这套机制的运行基础就是不间断的收集各个微服务应用的各项指标情况,并根据这些基础指标信息来制定监
控和预警规则,更进一步甚至作呕到哦一些自动画的运维操作等。

Spring Boot除了强大的快速开发功能之外,还因为 Starter POMs中提供了一个特殊依赖模块 ——
spring-boot-starter-actuator。
引入该模块能够自动为Spring Boot 构建订单应用提供一系列用于监控的端点。同时,Spring Cloud 在实
现各个微服务组件的时候,进一步为该模块做了不少扩展。

2.5.6.1初始actuator

引入模块:

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

启动服务后,访问 /health 查看监控信息。

注意:
如果访问/health 显示信息过少,如:{“status”:”UP”}, 其他/beans , /env 返回401 ,是由于权限限制。
需要在 application.properties,加入:management.security.enabled=false

在没有引入其他依赖之前,该的端点的内容较为简单,后续我们在使用Spring Cloud 的各个组件之后,它
的返回会变得非常丰富,这些内容将帮助我们制定更为个性化的监控策略。

2.5.6.2原生端点

根据端点的作用,可以将原生端点分为以下三大类:

  • 应用配置类:获取应用程序中加载的应用配置、环境变量、自动化配置报告等与Spring Boot应用密 切相关的配置类信息
  • 度量指标类:获取应用程序运行过程中用于监控的度量指标,比如内存信息、线程池信息、HTTP请求统计等。
  • 操作控制类:提供了对应用的关闭等操作类功能

    2.5.6.2.1应用配置类

    由于现在开发中使用Spring的配置,大多是通过包扫描和自动化配置,这使得这个应用的实例创建和依赖
    关系等信息被离散到了各个配置类的注解上,分析整个应用中资源和实例的各种关系变得非常困难。

这类端点可以帮助我们轻松获取一系列关于Spring应用配置内容的详细报告:

  • /autoconfig: 该端点用来获取应用的自动化配置报告,包括自动化配置的候选项。可以查看配置没有生效的原因
  • positiveMatches : 返回的是条件匹配成功的自动化配置
  • negativeMatches : 返回的是条件匹配不成功的自动化配置
  • /beans : 该端点用来获取应用上下文中创建的所有Bean.
    bean : Bean 的名称
    scope : Bean 的作用域
    type : Bean 的java类型
    resource : class 文件的具体路径
    dependencies : 依赖的bean名称
  • /configprops : 获取应用中配置的属性信息报告,关闭该端点:(endpoints.configprops.enabled=false)
    prefix : 属性的配置前缀
    properties : 各个属性的名称和值
  • /env : 获取应用所有可用的环境属性报告。
    包括环境变量、JVM属性、应用的配置属性、命令行中的参数。
    可以配合@ConfigurationProperties注解将它们引入达到应用程序中进行使用。
    另外,为了配置属性安全,可以配置password、secret、key这些关键词,这样该端点在返回它们的时候
    会使用 * 来替代实际的属性值。

  • /mappings : 返回所有Spring MVC 的控制器映射关系报告。
    bean: 该映射关系的请求处理器
    method:表示了该映射关系的具体处理类和处理函数

  • /info : 返回一些应用自定义的信息,默认只会返回一个空的JSON内容。
    可以在application.properties 配置文件中通过 info 前缀来设置一些属性,如:
info.bookName=spring
info.author=test

请求返回:{“author”:”test”,”bookName”:”spring”}

2.5.6.2.2 度量指标类

提供动态变化的,应用程序在运行过程中的一些快照信息,比如内存使用情况、HTTP请求统计、外部资源指标等,这些对监控系统非常有帮助。

  • /metrics : 返回当前应用的各类重要度量指标,如内存信息、线程信息、垃圾回收信息等
    gauge.* : HTTP请求的性能指标之一,反应一个绝对数值。gauge.response.hello”:7.0 ,表示上
    一次hello请求的延迟时间为5毫秒。
    counter.* : HTTP请求的性能指标之一,主要作为计数器来使用,记录了增加量和减少量。
    counter.status.200.hello : 11,代表了hello请求返回200状态的次数为11。
  • /health : 获取应用的各类健康指标信息。
    在spring-boot-starter-actuator 模块中国自带实现了一些常用资源的健康指标检测器,这些检测器都通过
    HealthIndicator接口实现,并且会根据依赖关系的引入实现自动化装配,例如:
    《Spring + Cloud微服务实战》读书笔记(二)————Spring Boot

自己实现一个采集健康信息的检测器:

@Component
public class TestHealth implements HealthIndicator{

    @Override
    public Health health() {
        int errorCode = check();
        if(errorCode != 0)
            return Health.down().withDetail("Error Code", errorCode).build();
        return Health.up().build();
    }

    private int check() {
        return 0;
    }

}

再访问/health ,返回结果:

{"status":"UP","testHealth":{"status":"UP"},"diskSpace":{"status":"UP","total":27808194560,"free":4236460032,"threshold":10485760}}
  • /dump : 暴露程序运行中的线程信息。它使用 java.lang.management.ThreadMXBean的
    dumpAllThreads 方法来返回所有含有同步信息的活动线程详情。
  • /trace : 返回基本的HTTP跟踪信息。默认情况下,跟踪信息的存储采用
    org.springframework.boot.actuate.trace.InMemoryTraceRepository实现的内存方式,始终保存最近
    的100条请求记录。

    2.5.6.2.3操作控制类

    在原生端点中,只提供了一个用来关闭应用的端点: /shutddown (在后续我们引入了Eureka之后,会引
    入更多控制端点)。可以通过配置开启:
    endpoints.shutdown.enabled=true
    之后通过post请求访问 /shutdown 可以关闭应用,真正在线上使用的时候,需要对其加入一定的保护机
    制,比如定制actuator的端点路径、这鞥个Spring Security进行安全校验等。