上一篇:《SpringCloud01—基础知识》
在构建springCloud框架之前,先通过本章了解一下用于构建微服务的基础框架–springBoot
在如今容器化大行其道的时代,Spring Boot除了可以很好融入Docker之外,其自身就支持嵌入式的Tomcat、Jetty 等容器。所以,通过Spring Boot构建的应用不再需要安装Tomcat,将应用打包成war,再部署到Tomcat这样复杂的构建与部署动作,只需将SpringBoot应用打成jar包,并通过java -jar命令直接运行就能启动一个标准化的Web应用,这使得Spring Boot应用变得非常轻便。
1.快速入门
1.1 快速搭建springBoot项目
按照以上步骤便可以快速搭建一个springBoot的web项目,目录结构如下所示
- src/main/java:主程序入口HelloApplication,可以通过直接运行该类来启动Spring Boot应用。
- src/main/resources:配置目录,该目录用来存放应用的一些配置信息,比如应用名、服务端口、数据库链接等。由于我们引入了Web模块,因此产生了static目录与templates目录,前者用于存放静态资源,如图片、CSS、JavaScript等;后者用于存放Web页面的模板文件,这里我们主要演示提供RESTful API,所以这两个目录并不会用到。
- src/test/:单元测试目录,生成的HelloApplicationTests通过JUnit 4实现,可以直接用运行Spring Boot应用的测试。后文中,我们会演示如何在该类中测试RESTful API。
1.2 Maven配置分析
打开当前工程下的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 https://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>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloud</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.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默认将该Web应用打包为jar的形式,而非 war的形式,因为默认的Web模块依赖会包含嵌入式的Tomcat,这样使得我们的应用jar自身就具备了提供Web服务的能力,后续我们会演示如何启动它。
父项目parent配置指定为spring-boot-starter-parent,该父项目中定义了Spring Boot版本的基础依赖以及一些默认配置内容,比如,配置文件application.properties的位置等。
在项目依赖dependencies配置中,包含了下面两项:
- spring-boot-starter-web:全栈 Web开发模块,包含嵌入式Tomcat、SpringMVC。
- 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 构建应用的时候,各项功能模块的整合不再像传统Spring应用的开发方式那样,需要在pom.xml中做大量的依赖配置
- Spring Boot的 Starter POMs采用spring-boot-starter-*的命名方式, *代表一个特别的应用功能模块,比如这里的web、test。
- 最后,项目构建的 build部分,引入了Spring Boot的 Maven插件,该插件非常实用,可以帮助我们方便地启停应用,这样在开发时就不用每次去找主类或是打包成jar来运行微服务,只需要通过mvn spring-boot:run命令就可以快速启动Spring Boot应用。
2.实现RESTful API
在Spring Boot中创建一个RESTful API的实现代码同Spring MVC应用一样,只是不需要像Spring MVC那样先做很多配置,而是像下面这样直接开始编写Controller内容:
- 新建package,命名为xx.xx.Controller,可根据实际的构建情况修改成自己的路径。
- 新建HelloController类,内容如下所示。
@RestController
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String inde(){
return "Hello SpringCloud";
}
}
- 启动该应用,通过浏览器访问http://localhost:8080/hello,我们可以看到返回了预期结果: Hello SpringCloud。
2.1 编写单元测试
在Spring Boot中实现单元测试同样非常方便,下面我们打开src/test/下的测试入口com.cloud.demo.DemoApplicationTests类,编写一个简单的单元测试来模拟HTTP请求,测试之前实现的/hello接口,该接口应返回Hello world字符串。
具体代码实现如下所示。
@SpringBootTest
class DemoApplicationTests {
private MockMvc mvc;
@Before
public void setUp() throws Exception {
mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
System.out.println("初始化mock模块");
}
@Test
void hello() throws Exception {
setUp();
String responseString = mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串;
System.out.println("获取结果为:" + responseString);
}
}
3.配置详解
在快速入门示例中,我们介绍 Spring Boot的工程结构时,提到过 src/main/resources目录是Spring Boot的配置目录,所以当要为应用创建个性化配置时,应在该目录下进行。
Spring Boot的默认配置文件位置为src/main/resources/application.properties。关于SpringBoot应用配置内容都可以集中在该文件中,根据我们引用的Starter模块不同,可以在这里定义容器端口号,数据库连接信息、日志级别等。
比如,我们需要自定义Web模块的服务端口号,可以在application.properties中添加server.port=8888来指定服务端口为8888 ,也可以通过spring.application.name=hello来指定应用名(该名字在后续Spring Cloud中会被注册为服务名)。
Spring Boot的配置文件除了可以使用传统的properties文件之外,还支持现在被广泛推荐使用的YAML文件。
YAML采用的配置格式不像properties 的配置那样以单纯的键值对形式来表示,而是以类似大纲的缩进形式来表示。下面是一段YAML 配置信息:
environments:
dev:
url: http://dev.bar.com
name: Developer Setep
prod:
url: http://foo.bar.com
name: My Cool App
与其等价的properties配置如下:
environments.dev.url= http://dev.bar.com
environments.dev.name= Developer Setep
environments.prod.url= http://foo.bar.com
environments.prod.name= My Cool App
虽然通过YAML的配置方式我们可以看到,配置信息利用阶梯化缩进的方式,其结构更为清晰易读,同时配置内容的字符量也得到显著减少。但是YAML目前还有一些不足,它无法通过@PropertySource注解来加载配置。
值得注意的是,YAML将属性加载到内存中保存的时候是有序的,所以当配置文件中的信息需要具备顺序含义时,YAML 的配置方式比起properties 配置文件更有优势。
4.自定义参数
除了可以在Spring Boot 的配置文件中设置各个Starter模块中预定义的配置属性,也可以在配置文件中定义一些我们需要的自定义属性。比如在 application.properties中添加:
book.name=SpringCloud
book.auth=ninesun
然后在实体类中使用@Value注解加载这些参数
@Component
public class Book {
@Value("${book.name}")
private String name;
@Value("${book.auth}")
private String auth;
}
5.参数引用
在application.properties 中的各个参数之间可以直接通过使用PlaceHolder的方式来进行引用,就像下面的设置:
book.name=SpringCloud
book.auth=ninesun
book.desc=${book.name} is writing 《${book.name}》
book.desc参数引用了上文中定义的book.name和 book.author属性,最后该属性的值就是ninesun is writing 《springCloud》。
6.使用随机数
在一些特殊情况下,我们希望有些参数每次被加载的时候不是一个固定的值,比如密钥、服务端口等。在 Spring Boot的属性配置文件中,可以通过使用$ {random}配置来产生随机的int 值、long值或者string字符串,这样我们就可以容易地通过配置随机生成属性,而不是在程序中通过编码来实现这些逻辑。
# 随机字符串
com.cloud.demo.value=${random.value}
# 随机int
com.cloud.demo.number=${random.int}
# 随机long
com.cloud.demo.bignumber=${random.long}
# 10以内的随机数
com.cloud.demo.test1=${random.int(10)}
# 10-20的随机数
com.cloud.demo.test2=${random.int[10,20])}
7.多环境配置
我们在开发应用的时候,通常同一套程序会被应用和安装到几个不同的环境中,比如开发、测试、生产等。其中每个环境的数据库地址、服务器端口等配置都不同,如果在为不同环境打包时都要频繁修改配置文件的话,那必将是个非常烦琐且容易发生错误的事。
对于多环境的配置,各种项目构建工具或是框架的基本思路是一致的,通过配置多份不同环境的配置文件,再通过打包命令指定需要打包的内容之后进行区分打包,Spring Boot也不例外,或者说实现起来更加简单。
在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配置文件内容。
下面,以不同环境配置不同的服务端口为例,进行样例实验。
针对各环境新建不同的配置文件application-dev.properties 、application-test.properties、application-prod.properties。在这三个文件中均设置不同的server.port属性, - dev环境设置为1111
- test环境设置为2222
- prod环境设置为3333。
application.properties 中设置spring.profiles.active=dev,意为默认以dev环境设置。测试结果如下:
所以我们如果想添加其他环境只需要在application-x 来配置,然后在application.properties文件中进行激活即可
8.监控与管理
在微服务架构中,我们将原本庞大的单体系统拆分成多个提供不同服务的应用。虽然各个应用的内部逻辑因分解而得以简化,但是由于部署应用的数量成倍增长,使得系统的维护复杂度大大提升。对于运维人员来说,随着应用的不断增多,系统集群中出现故障的频率也变得越来越高,虽然在高可用机制的保护下,个别故障不会影响系统的对外服务,但是这些频繁出现的故障需要被及时发现和处理才能长期保证系统处于健康可用状态。为了能对这些成倍增长的应用做到高效运维,传统的运维方式显然是不合适的,所以我们需要实现一套自动化的监控运维机制,而这套机制的运行基础就是不间断地收集各个微服务应用的各项指标情况,并根据这些基础指标信息来制定监控和预警规则,更进一步甚至做到一些自动化的运维操作等。
为了让运维系统能够获取各个微服务应用的相关指标以及实现一些常规操作控制,我们需要开发一套专门用于植入各个微服务应用的接口供监控系统采集信息。而这些接口往往有很大一部分指标都是类似的,比如环境变量、垃圾收集信息、内存信息、线程池信息等。既然这些信息那么通用,难道就没有一个标准化的实现框架吗?
当我们决定用Spring Boot来作为微服务框架时,除了它强大的快速开发功能之外,还因为它在Starter POMs中提供了一个特殊依赖模块spring-boot-starter-actuator。引入该模块能够自动为Spring Boot构建的应用提供系列用于监控的端点。
同时,SpringCloud 在实现各个微服务组件的时候,进一步为该模块做了不少扩展,比如,为原生端点增加了更多的指标和度量信息(比如在整合 Eureka的时候会为/health端点增加相关的信息),并且根据不同的组件还提供了更多有空的端点(比如,为API网关组件Zuul提供了/routes端点来返回路由信息)。
spring-boot-starter-actuator模块的实现对于实施微服务的中小团队来说,可以有效地省去或大大减少监控系统在采集应用指标时的开发量。当然,它也并不是万能的,有时候也需要对其做一些简单的扩展来帮助我们实现自身系统个性化的监控需求。所以,在本节将详细介绍一些关于spring-boot-starter-actuator模块的内容,包括原生提供的端点以及一些常用的扩展和配置方式等。
8.1 初识actuator
添加依赖即可引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加依赖重启项目之后我们可以看到
说明监控页面的基地址是/actuator,我们在浏览器端打开地址http://localhost:1111/actuator/,可以看到
8.2 原生端点
通过在快速入门示例中添加 spring-boot-starter-actuator模块,我们已经对它有了一个初步的认识。接下来,我们详细介绍一下spring-boot-starter-actuator模块中已经实现的一些原生端点。根据端点的作用,可以将原生端点分为以下三大类:
- 应用配置类:获取应用程序中加载的应用配置、环境变量、自动化配置报告等与Spring Boot应用密切相关的配置类信息
- 度量指标类:获取应用程序运行过程中用于监控的度量指标,比如内存信息、线程池信息、HTTP请求统计等。
- 操作控制类:提供了对应用的关闭等操作类功能。
下面我们来详细了解一下这三类端点都分别可以为我们提供怎样的有用信息和强大功能,以及我们如何去扩展和配置它们。
在配置文件中添加
management.endpoints.web.exposure.include=*
8.2.1 应用配置类
由于Spring Boot为了改善传统Spring应用繁杂的配置内容,采用了包扫描和自动化配置的机制来加载原本集中于XML 文件中的各项内容。虽然这样的做法让我们的代码变得非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上,这使我们分析整个应用中资源和实例的各种关系变得非常困难。
而这类端点可以帮助我们轻松获取一系列关于Spring应用配置内容的详细报告,比如自动化配置的报告、Bean 创建的报告、环境属性的报告等。
-
/conditions:该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项。同时还列出了每个候选项是否满足自动化配置的各个先决条件。所以,该端点可以帮助我们方便地找到一些自动化配置为什么没有生效的具体原因。该报告内容将自动化配置内容分为以下两部分。
访问链接:http://localhost:1111/actuator/conditions- positiveMatches中返回的是条件匹配成功的自动化配置。
- negativeMatches中返回的是条件匹配不成功的自动化配置。
-
/beans:该端点用来获取应用上下文中创建的所有Bean
访问链接:http://localhost:1111/actuator/beans
在如上示例中,我们可以看到在每个Bean中都包含了下面这些信息。
- bean: Bean的名称。
- scope: Bean的作用域。
- type: Bean的 Java类型。
- resource: class 文件的具体路径。
- dependencies:依赖的 Bean名称。
还有很多端点,此处就不一一列举有需要可以自行去了解。
下一篇:《SpringCloud03—服务治理:SpringCloud Eureka》