Spring Cloud简介

Spring Cloud是一个基于SpringBoot实现的微服务架构开发工具。它为微服务架构中涉及的配置管理、 服务治理、 断路器、 智能路由、 微代理、 控制总线、 全局锁、 决策竞选、 分布式会话和集群状态管理等操作提供了一种简单的开发方式。
Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品,还可能会新增),如下所述。

  • Spring Cloud Config: 配置管理工具,支持使用Git存储配置内容,可以使用它实现应用配置的外部化存储,并支持客户端配置信息刷新、 加密/解密配置内容等。
  • Spring Cloud Netflix: 核心组件,对多个Netflix OSS开源套件进行整合。
    Eureka: 服务治理组件,包含服务注册中心、 服务注册与发现机制的实现。
    Hystrix: 容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障 提供强大的容错能力。
    ribbon: 客户端负载均衡的服务调用组件。
    Feign: 基于ribbon和Hystrix 的声明式服务调用组件。
    Zuul: 网关组件, 提供智能路由、 访问过滤等功能。
    Archaius: 外部化配置组件。
  • Spring Cloud Bus: 事件、 消息总线, 用于传播集群中的状态变化或事件, 以触发后续的处理, 比如用来动态刷新配置等。
  • Spring Cloud Cluster: 针对ZooKeeper、 Redis、 Hazelcast、 Consul的选举算法和通用状态模式的实现。
  • Spring Cloud Cloudfoundry: 与PivotalCloudfoundry的整合支持。
  • Spring Cloud Consul: 服务发现与配置管理工具。
  • Spring Cloud Stream: 通过Redis、 Rabbit或者Kafka实现的消费微服务, 可以通过简单的声明式模型来发送和接收消息。
  • Spring Cloud AWS: 用千简化整合AmazonWeb Service的组件。
  • Spring Cloud Security: 安全工具包, 提供在Zuul代理中对0Auth2 客户端请求的中继器。
  • Spring Cloud Sleuth: Spring Cloud应用的分布式跟踪实现, 可以完美整合Zipkin。
  • Spring Cloud ZooKeeper: 基于ZooKeeper的服务发现与配置管理组件。
  • Spring Cloud Starters: Spring Cloud的基础组件, 它是基于Spring Boot风格项目的 基础依赖模块。
  • Spring Cloud CLI: 用于在Groovy中快速创建Spring Cloud应用的Spring Boot CLI 插件。

微服务构建: Spring Boot

在展开Spring Cloud的微服务架构部署之前, 我们先来了解一下用于构建微服务的基础框架SpringBoot。对于SpringBoot 已经有深入了解的同学可以直接跳过, 进入后续学习SpringCloud各个组件的使用。
在次将介绍下面这些与后续介绍有密切联系的内容:
• 如何构建SpringBoot项目 • 如何实现RESTfulAPI接口
• 如何实现多环境的SpringBoot应用配置
• 深入理解SpringBoot配置的启动机制
• Spring Boot应用的监控与管理
• Spring Cloud微服务实战
更多关于Spring Boot的使用细节,可以参阅Spring Boot的官方文档或是其他资料来进一步学习。

spring boot 简介

对于很多Spring框架的初学者来说, 经常会因为其繁杂的配置文件而却步。 而对于很多老手来说, 每次新构建项目总是会重复复制粘贴一 些差不多的配置文件这样枯燥乏味的事。 作为一名优秀的程序员或架构师, 我们总会想尽办法来避免这样的重复劳动, 比如, 通过Maven等构建工具来创建针对不同场景的脚手架工程,在需要新建项目时通过这些脚手架来初始化我们自定义的标准工程, 并根据需要做一 些简单修改以达到简化原有配置过程的效果。 这样的做法虽然减少了工作量, 但是这些配置依然大量散布在我们的工程中, 大部分情况下我们并不会去修改这些内容, 但为什么还要反复出现在我们的工程中呢?

Spring Boot的出现 可以有效改善这类问题,SpringBoot的宗旨并非要重写Spring或是 替代Spring, 而是希望通过设计大量的自动化配置等方式来简化Spring原有样板化的配置, 使得开发者可以快速构建应用。

除了解决配置问题之外,SpringBoot还通过一系列StaiterPOMs的定义, 让我们整合各项功能的时候, 不需要在 Maven的pom.xml中维护那些错综复杂的依赖关系, 而是通过类似模块化的Starter模块定义来引用, 使得依赖管理工作变得更为简单。

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

Spring Boot对于构建、 部署等做了这么多的优化, 自然不能少了对开发环节的优化。 整个SpringBoot的生态系统都使用到了Groovy, 很自然的,我们完全可以通过使用Gradle 和Groovy来开发Spring Boot应用, 比如下面短短的不足100个字符的代码, 通过编译打包, 使用java -jar命令就能启动一个返回"hello"的RSETfulAPI。

@RestController 
class App { 
	@RequestMapping("/") 
	String home() { 
		"hello" 
	}
}

说了这么多Spring Boot带来的颠覆性框架特性,下面我们就通过后续内容来体验一下 使用Spring Boot构建微服务的过程, 以对Spring Boot有一个直观的感受。

spring boot 快速入门

在本节中, 我将逐步创建一个Spring Boot的基础项目, 并且实现一个简单 的RESTful API 通过这个例子对Spring Boot有一个初步的了解, 并体验其结构简单、 开发迅速的特性。

系统及工具版本要求:

环境:JDK1.8、Spring Framework 4.2.7及以上版本 、Maven 3.2及以上版本/Gradle 1.12及以上版本
工具:intellij idea

项目构建与解析

利用IDEA创建一个简单的spring boot项目,spring boot项目搭建可以参考我之前的博客或者自行参考网上资料;

服务治理springcloud nacos gateway微服务架构 spring cloud微服务架构实战_Cloud


项目搭建目录结构如上图所示,Spring Boot的基础结构有三大块(具体路径根据用户生成项目时填写的Group 和Artifact有所差异)。

• src/main/java: 主程序入口 HelloApp巨ca巨on, 可以通过直接运行该类来 启动Spring Boot应用。

• src/main/resources: 配置目录, 该目录用来存放应用的一些配置信息, 比如 应用名、服务端口、数据库链接等。由千我们引入了Web模块,因此产生了static 目录与templates目录, 前者用于存放静态资源, 如图片、 css、JavaScript等; 后者用于存放Web页面的模板文件, 这里我们主要演示提供RESTfulAPL所以这 两个目录并不会用到。

• src/test/: 单元测试目录, 生成的HelloApplica巨onTests通过JUnit 4实 现, 可以直接用运行SpringBoot应用的测试。 后文中, 我们会演示如何在该类中测 试RESTfulAPI。

maven配置分析

打开当前工程下的pom.xml文件, 看看生成的项目都引入了哪些依赖来构建Spring Boot工程, 内容大致如下所示。

<?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.huaxia</groupId>
    <artifactId>spring-boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot</name>
    <description>Demo project for Spring Boot</description>

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

在基础信息部分, groupid和 artifactId 对应生成项目时页面上输入的内容。 另外, 我们还可以注意到, 打包形式为jar: <packaging>jar</packaging>, 正如我们之前所介绍的,SpringBoot默认将该Web应用打包为jar 的形式, 而非war 的形式, 因为 默认 的Web模块依赖 会包含嵌入式的Tomcat , 这样使得我们的应用jar自身就具备了提供 Web服务的能力, 后续我们会演示如何启动它。
父项目parent配置指定为 spring-boot-starter-parent的2.1.6版本,该父项 目中定义了Spring Boot版本的基础依赖以及一些默认配置内容, 比如,配置文件 application.properties的位置等。 在项目依赖 dependencies配置中, 包含了下面两项。
• spring-boot-starter-web: 全栈Web开发模块, 包含嵌入式Tomcat、 Spring MVC。
• spring-boot-starter士est: 通用测试模块, 包含JUnit、 Hamcrest、 Mockito 。
这里所引用的web和test 模块,在SpringBoot 生态中被称为Starter POMs。Starter POMs 是一系列轻便的依赖 包, 是一套一站式的Spring相关技术的解决方案。 开发者在使用和整 合模块时, 不必再去搜寻样例代码中的依赖配置来复制使用,只需要引入对应的模块包即 可。比如, 开发Web应用的时候, 就引入spring-boot-starter-web, 希望应用具备 访问数据库能力的时候, 那就再引入 spring-boot-starter-jdbc 或是更好用的 spring-boot-starter-data-jpa。 在使用SpringBoot构建应用的时候, 各项功能模 块的整合不再像传统Spring应用的开发方式那样,需要在 pom.xml中做大量的依赖配置,而是通过使用StarterPOMs定义的依赖包,使得功能模块整合变得非常轻巧,易于理解与使用。
Spring Boot的StarterPOMs采用spring-boot-starter-*的命名方式,*代表一个 特别的应用功能模块,比如这里的web、test。SpringBoot工程本身的结构非常简单,大量 的学习要点还是将来在对这些StarterPOMs的使用之上。 在本笔记中, 由于主要讲述Spring Cloud的微服务组件内容,因此对各个Spring Boot的模块内容不做详尽讲解。 对于初学者 来说,我们也不必一次性地将所有模块的详细用法都掌握牢固,只需了解每个模块能做什么即可。
最后,项目构建的 build部分, 引入了SpringBoot的Maven插件,该插件非常实用, 可以帮助我们方便地启停应用,这样在开发时就不用每次去找主类或是打包成jar来运行微服务,只需要通过mvn spring-boot:run 命令就可以快速启动SpringBoot应用。

实现RESTfulAPI

在Spring Boot中创建一个RESTfulAPI的实现代码同SpringMVC应用一样,只是不 需要像SpringMVC那样先做很多配置,而是像下面这样直接开始编写Controller内容:
• 新建package, 命名为com.huaxia.web, 可根据实际的构建情况修改成自 己的路径。
• 新建HelloController类,内容如下所示。

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String index(){
        return "hello world";
    }
}

• 启动该应用,通过浏览器访问http:/ /localhost: 8080/hello, 我们可以看到返回了预期结果: Hello World。

启动Spring Boot应用

• 作为一个 Java 应用程序, 可以直接通过运行拥有 main 函数的类来启动。
• 在 Maven 配置中,之前提到了spring-boot 插件,可以使用它来启动, 比如执行 mvn spring-boot:run 命令,或是直接单击 IDEA 中对 Maven 插件的工具
• 在服务器上部署运行时, 通常先使用 mvn install 将应用打包成 jar 包, 再通过 j ava -jar xxx. jar来启动应用。

编写单元测试

功能实现之后,我们要养成随手写配套单元测试的习惯,这在微服务架构中尤为重要。 通常,我们实施微服务架构的时候, 已经实现了前后端分离的项目与架构部署。 那么在实现后端服务的时候, 单元测试是在开发过程中用来验证代码正确性非常好的手段, 并且这些单元测试将会很好地支持我们未来可能会进行的重构。
在 Spring Boot 中实现单元测试同样非常方便, 下面我们打开 src/test/下的测试入口 com.huaxia.ApplicationTests 类, 编写一个简单的单元测试来模拟 HTTP请求, 测试之前实现的/hello 接口, 该接口应返回 Hello World 字符串。
具体代码实现如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@WebAppConfiguration
public class ApplicationTests {

    private MockMvc mvc;

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

    @Test
    public void contextLoads() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/hello")).andDo(MockMvcResultHandlers.print())
                .andReturn();
    }

}

代码解析如下

• @RunWith(SpringJUn辽4ClassRunner.class): 引入Spring对JUnit4的支 持。
• @SpringBootTest: 指定SpringBoot的启动类。
• @WebAppConfiguration: 开启Web应用的配置, 用于模拟ServletContext。
• MockMvc对象: 用于模拟调用 Controller的接口发起请求, 在@Test定义的hello 测试用例中, perform函数执行一次请求调用, andExpect用于判断接口返回的期望值。
• @Before: JUnit中定义在测试用例@Test内容执行前预加载的内容, 这里用来初始化对HelloController的模拟 。

模拟结果

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /hello
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.huaxia.web.HelloController
           Method = public java.lang.String com.huaxia.web.HelloController.index()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"text/plain;charset=ISO-8859-1", Content-Length:"11"]
     Content type = text/plain;charset=ISO-8859-1
             Body = hello world
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

配置详解

在上面, 我们轻松地实现了一个简单的RESTfulAPI应用, 体验了SpringBoot的 诸多优点。我们用非常少的代码就成功实现了一个Web应用,这是传统Spring应用无法办到的。虽然在实现Controller时用到的代码是一样的,但是在配置方面,相信大家也注意到 了, 在上面的例子中, 除了Maven的配置之外, 没有引入任何其他配置。
这就是之前我们提到的,SpringBoot针对常用的开发场景提供了一系列自动化配置来 减少原本复杂而又几乎很少改动的模板化配置内容。但是,我们还是需要了解如何在Spring Boot中修改这些自动化的配置内容, 以应对一些特殊的场景需求, 比如, 我们在同一台主 机上需要启动多个基千Spring Boot的Web应用, 若不为每个应用指定特别的端口号, 那 么默认的8080 端口必将导致冲突。
后续我们在使用SpringCloud的各个组件的时候, 其实有大量的工作都会是针对配置 文件的。所以我们有必要深入了解一些关于SpringBoot中的配置文件的知识, 比如配置方式、 如何实现多环境配置、 配置信息的加载顺序等。

配置文件

在快速入门示例中, 我们介绍Spring Boot 的工程结构时, 提到过 src/rnain/ resources 目录是SpringBoot的配置目录, 所以当要为应用创建个性化配置时, 应在该目录下进行。

Spring Boot 的默认配置文件位置为 src/main/resources/application. properties 。关于SpringBoot应用的配置内容都可以集中在该文件中, 根据我们引入的 不同Starter模块,可以在这里定义容器端口号、 数据库连接信息、 日志级别等各种配置信 息。比如,我们需要自定义Web模块的服务端口号,可以在application. properties中添加 server.port=8888 来指定服务端口为 8888 , 也可 以通过 spring.application.name =hello 来指定应用名(该名字在后续SpringCloud中会被 注册为服务名)。

Spring Boot的配置文件除了可以使用传统的 properties文件之外,还支持现在被广泛推荐使用的YAML文件。

YAML 采用的配置格式不像 properties 的配置那样以单纯的键值对形式来表示,而是以类似大纲的缩进形式来表示。 下面是一段 YAML配置信息:

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

与其等价的 properties配置如下所示:

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

通过 YAML 的配置方式我们可以看到,配置信息利用阶梯化缩进的方式, 其结构更为清晰易读, 同时配置内容的字符量也得到显著减少。 除此之外,YAML 还可以在一个单个件中通过使用 spring.profiles 属性来定义多个不同的环境配置。 例如下面的内容, 在指定为 test环境时,server.port将使用8882端口;而在prod环境中,server.port 将使用8883端口;如果没有指定环境, server.port将使用8881端口。

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

需要注意 YAML 目前还有—些不足,它无法通过@PropertySource 注解来加载配置。 但是, YAML 将属性加载到内存中保存的时候是有序的, 所以当配置文件中的信息需要具备顺序含义时,YAML的配置方式比起 properties配置文件更有优势。

自定义参数

除了可以在Spring Boot 的配置文件中设置各个Starter 模块中预定义的配置属性, 也可以在配置文件中定义一些我们需要的自定义属性。 比如在 application.properties 中 添加:

book.name=SpringCloudinAction 
book.author=ZhaiYongchao

然后, 在应用中可以通过@Value 注解来加载这些自定义的参数, 比如:

@Component 
public class Book {

    @Value("${book.name}")
    private String name;
    @Value("${book.author}")
    private String author;
    
    //省略getter和setter
}

@Value 注解加载属性值的时候可以支持两种表达式来进行配置, 如下所示。
• 一种是上面介绍的 PlaceHolder 方式, 格式为${...}, 大括号内为 PlaceHolder。
• 另一种是使用SpEL表达式 (SpringExpression Language), 格式为#{...}, 大括号 内为SpEL表达式。

参数引用

在 application.properties 中的各个参数之间可以直接通过使用PlaceHolder的方式来进行引用 , 就像下面的设置:

book.name=SpringCloud
book.author=ZhaiYongchao
book.desc=${book.author} is writing《${book.name}》

book.desc 参数引用了上文中定义的book.name和book.author 属性, 最后该属性的值就是ZhaiYongchao is writng《SpringCloud》。

使用随机数

在 一些特殊情况下, 我们希望有些参数每次被加载的时候不是一个固定的值, 比如密钥、 服务端口等。 在SpirngBoot的属性配置文件中, 可以 通过使用${random}配置来产生随机的int值、long值或者string字符串,这样我们就可以容易地通过配置随机生成属性, 而不是在程序中通过编码来实现这些逻辑。

${random}的配置方式主要有以下几种:
#随机字符串
com.didispace.blog.value=${random.value}
#随机int 
com.didispace.blog.number=${random.int}
#随机long
com.didispace.blog.bignumber=${random.long}
#10以内的随机数 
com.didispace.blog.test1=${random.int(l0)}
#10-20的随机数 
com.didispace.blog.test2=${random.int[l0,20]}

该配置方式可以 设置应用 端口等场景, 以避免在本地调试时出现端口冲突的麻烦。

命令行参数

在上面的 “快速入门 ” 中, 我们还介绍了如何启动SpirngBoot应用, 其中提到了使用命令java -jar 来启动的方式。 该命令除了启动应用之外, 还可以在命令行中指定应用的参数, 比如java -jar xxx.jar --server.part=8888, 直接以命令行的方式来设server.port属性, 并将启动应用的端口设为8888。
在用命令行方式启动SpringBoot 应用时, 连续的两个减号--就是对 application.properties 中的属性值进行赋值 的标识。 所以 , java -jar xxx.jar–server.port=8888命令, 等价于在 application.properties 中添加属性server.port=8888。
通过命令行来修改属性值是 SpringBoot非常重要的一个特性。 通过此特性,理论上已经使得应用的属性在启动前是可变的, 所以其中的端口号也好、 数据库连接也好, 都是可以在应用启动时发生改变的, 而不同于以往的Spring应用通过Maven的Profile在编译器中进行不同环境的构建。 SpringBoot的这种方式, 可以让应用程序的打包内容贯穿开发、 测试以及线上部署, 而Maven不同Profile的方案为每个环境所构建的包,其内容本质上是 不同的。 但是, 如果每个参数都需要通过命令行来指定, 这显然也不是一个好的方案, 所以下面我们看看如何在SpringBoot中实现多环境的配置。

多环境配置

我们在开发应用的时候, 通常同一套程序会被应用和安装到几个不同的环境中, 比如 开发、 测试、 生产等。 其中每个环境的数据库地址、 服务器端口等配置都不同, 如果在为不同环境打包时都要频繁修改配置文件的话, 那必将是个非常烦琐且容易发生错误的事。
对于多环境的配置, 各种项目构建工具或是框架的基本思路是一致的, 通过配置多份不同环境的配置文件,再通过打包命令指定需要打包的内容之后进行区分打包,SpringBoot 也不例外, 或者说实现起来更加简单。
在 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。

• app巨cation.properties中设置 spring.profiles.active=dev, 意为默 认以dev环境设置。

• 测试不同配置的加载。

• 启动springboot,在日志中可以看到启动的端口;

服务治理springcloud nacos gateway微服务架构 spring cloud微服务架构实战_Cloud_02

加载顺序

在上面的例子中, 我们将SpringBoot应用需要的配置内容都放在了项目工程中, 已经 能够通过spring.profiles.active或是通过Maven来实现多环境的支待。 但是, 当团队逐渐壮大, 分工越来越细致之后, 往往不需要让开发人员知道测试或是生产环境的细节, 而是希望由每个环境各自的负责人(QA或是运维)来集中维护这些信息。 那么如果 还是以这样的方式存储配置内容, 对于不同环境配置的修改就不得不去获取工程内容来修改这些配置内容, 当应用非常多的时候就变得非常不方便。 同时, 配置内容对开发人员都可见, 这本身也是一种安全隐患。 对此, 出现了很多将配置内容外部化的框架和工具, 后续将要介绍SpringCloud Config就是其中之一, 为了后续能更好地理解Spring Cloud Config的加载机制, 我们需要对SpringBoot对数据文件的加载机制有一定的了解。
为了能够更合理地重写各属性的值,SpringBoot使用了下面这种较为特别的属性加载顺序:

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

优先级按上面的顺序由高到低,数字越小优先级越高。
可以看到,其中第7项和第9项 都是从应用jar包之外读取配置文件,所以,实现外部 化配置的原理就是从此切入,为其指定外部配置文件的加载位置来取代jar包之内的配置内 容。 通过这样的实现,我们的工程在配置中就变得非常干净,只需在本地放置开发需要的 配置即可,而不用关心其他环境的配置,由其对应环境的负责人去维护即可。

监控与管理

在微服务架构中,我们将原本庞大的单体系统拆分成多个提供不同服务的应用。 虽然 各个应用的内部逻辑因分解而得以简化,但是由于部署应用的数量成倍增长,使得系统的 维护复杂度大大提升。 对于运维人员来说,随着应用的不断增多,系统集群中出现故障的 频率也变得越来 越高,虽然在高可用机制的保护下,个别故障不会影响系统的对外服务, 但是这些频繁出现的故障需要被及时发现和处理才能长期保证系统处千健康可用状态。 为 了能对这些成倍增长的应用做到高效运维,传统的运维方式显然是不合适的,所以我们需 要实现一套自动化的监控 运维机制,而这套机制的运行基础就是不间断地收集各个微服务 应用的各项 指标情况,并根据这些基础指标信息来制定监控和预警规则,更进一步甚至做 到一些自动化的运维操作等。

为了让运维系统能够获取各个微服务应用的相关指标以及实现一些常规操作控制,我 们需要开发一套专门用于植入各个微服务应用的接口供监控系统采集信息。 而这些接口往往有很大一部分指标都是类似的,比如环境变量、 垃圾收集信息、 内存信息、 线程池信息 等。 既然这些信息那么通用,难道就没有一个标准化的实现框架吗?

当我们决定用SpringBoot来作为微服务框架时,除了它强大的快速开发功能之外,还因 为它在StarterPOMs中提供了一个特殊依赖模块spring-boot-starter-actuator 。引入该模块能够自动为 Spring Boot 构建的应用提供一系列用于监控的端点。 同时,Spring Cloud 在实现各个微服务组件的时候,进一步为该模块做了不少扩展, 比如,为原生端点增加了更多的指标和度量信息(比如在整合 Eureka 的时候会为/health 端点增加相关的 信息),并且根据不同的组件还提供了更多有空的端点(比如,为 API 网关组件Zuul 提供 了/routes 端点来返回路由信息)。

spring-boot-starter-actuator 模块的实现对千实施微服务的中小团队来说, 可以有效地省去或大大减少监控系统在采集应用指标时的开发量。 当然,它也并不是万能 的,有时候也需要对其做一些简单的扩展来帮助我们实现自身系统个性化的监控需求。 所 以,在本节将详细介绍一些关于 spring-boot-starter-actuator模块的内容,包括 原生提供的端点以及一些常用的扩展和配置方式等。

初识actuator

下面,我们通过对 “快速入门 ” 小节中实现的 Spring Boot应用增加 spring-boot- starter-actuator 模块功能,来对它有一个直观的认识。

在现有的 Spring Boot 应用中引入该模块非常简单, 只需要在 pom.xml 的 dependency 节点中,新增 spring-boot-starter-actuator 的依赖即可,具体如下:

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

然后在application.properties中添加如下配置:

#修改访问路径  2.0之前默认是/   2.0默认是 /actuator  可以通过这个属性值修改
management.endpoints.web.base-path=/
#开放所有页面节点  默认只开启了health、info两个节点
management.endpoints.web.exposure.include=*
#显示健康具体信息  默认不会显示详细信息
management.endpoint.health.show-details=always

重新启动应用,启动后浏览器输入 http://localhost:1111/actuator,我们可以获得如下信息:

{
	"_links": {
		"self": {
			"href": "http://localhost:1111/actuator",
			"templated": false
		},
		"auditevents": {
			"href": "http://localhost:1111/actuator/auditevents",
			"templated": false
		},
		"beans": {
			"href": "http://localhost:1111/actuator/beans",
			"templated": false
		},
		"caches-cache": {
			"href": "http://localhost:1111/actuator/caches/{cache}",
			"templated": true
		},
		"caches": {
			"href": "http://localhost:1111/actuator/caches",
			"templated": false
		},
		"health-component": {
			"href": "http://localhost:1111/actuator/health/{component}",
			"templated": true
		},
		"health": {
			"href": "http://localhost:1111/actuator/health",
			"templated": false
		},
		"health-component-instance": {
			"href": "http://localhost:1111/actuator/health/{component}/{instance}",
			"templated": true
		},
		"conditions": {
			"href": "http://localhost:1111/actuator/conditions",
			"templated": false
		},
		"configprops": {
			"href": "http://localhost:1111/actuator/configprops",
			"templated": false
		},
		"env": {
			"href": "http://localhost:1111/actuator/env",
			"templated": false
		},
		"env-toMatch": {
			"href": "http://localhost:1111/actuator/env/{toMatch}",
			"templated": true
		},
		"info": {
			"href": "http://localhost:1111/actuator/info",
			"templated": false
		},
		"loggers": {
			"href": "http://localhost:1111/actuator/loggers",
			"templated": false
		},
		"loggers-name": {
			"href": "http://localhost:1111/actuator/loggers/{name}",
			"templated": true
		},
		"heapdump": {
			"href": "http://localhost:1111/actuator/heapdump",
			"templated": false
		},
		"threaddump": {
			"href": "http://localhost:1111/actuator/threaddump",
			"templated": false
		},
		"metrics-requiredMetricName": {
			"href": "http://localhost:1111/actuator/metrics/{requiredMetricName}",
			"templated": true
		},
		"metrics": {
			"href": "http://localhost:1111/actuator/metrics",
			"templated": false
		},
		"scheduledtasks": {
			"href": "http://localhost:1111/actuator/scheduledtasks",
			"templated": false
		},
		"httptrace": {
			"href": "http://localhost:1111/actuator/httptrace",
			"templated": false
		},
		"mappings": {
			"href": "http://localhost:1111/actuator/mappings",
			"templated": false
		}
	}
}

上图显示的端点定义, 这端点并非我们自己在程序中创建的,而是由 spring-boot-starter-actuator 模块根据应用依赖和配置自动创建出来的监控和管理端点。通过这些端点,我们可以实时获取应用的各项监控指标,比如访间/actuator/health端点。

{
	"status": "UP",
	"details": {
		"diskSpace": {
			"status": "UP",
			"details": {
				"total": 157285347328,
				"free": 137876561920,
				"threshold": 10485760
			}
		}
	}
}

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

原生端点

根据端点的作用, 可以将原生端点分为以下三大类。
• 应用配置类:获取应用程序中加载的应用配置、 环境变量、 自动化配置报告等与 Spring Boot应用密切相关的配置类信息。
• 度最指标类:获取应用程序运行过程中用于监控的度量指标, 比如内存信息、 线程 池信息、 HTTP请求统计等。
• 操作控制类:提供了对应用的关闭等操作类功能。
下面我们来详细了解一下这三类端点都分别可以为我们提供怎样的有用信息和强大功 能, 以及我们如何去扩展和配置它们。

应用配置类

由于SpringBoot为了改善传统Spring应用繁杂的配置内容,采用了包扫描和自动化配 置的机制来加载原本集中于XML文件中的各项内容。 虽然这样的做法让我们的代码变得 非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上, 这使我们分析整个应用中资源和实例的各种关系变得非常困难。 而这类端点可以帮助我们 轻松获取一系列关于Spring应用配置内容的详细报告, 比如自动化配置的报告、 Bean创建的报告、 环境属性的报告等。

度量指标类

度量指标类端点提供的报告内容则是动态变 化的,这些端点提供了应用程序在运行过程中的一些快照信息,比如内存使用情况、 HTTP 请求统计、外部资源指标等。这些端点对于我们构建微服务架构中的监控系统非常有帮助, 由于 Spring Boot 应用自身实现了这些端点,所以我们可以很方便地利用它们来收集我们想 要的信息,以制定出各种自动化策略。

操作控制类

操作 控制类端点拥有更强大的控制能力,如果要使用它们的话,需要通过属性来配置开启操作。 在原生端点中, 只提供了一个用来关闭应用的端点: /shutdown (在后续我们引入了 Eureka之后,会引入更多控制端点)。 可以通过如下配置开启它:
endpoints.shutdown.enabled=true
在配置了上述属性之后,只需要访问该应用的/shutdown端点就能实现关闭该应用的 远程操作。 由于开放关闭应用的操作本身是一件非常危险的事,所以真正在线上使用的时 候,需要对其加入一定的保护机制,比如定制actuator的端点路径、 整合SpringSecurity进 行安全校验等。

小结

本篇我们通过构建一个最基本的SpringBoot工程,让大家对其有了一个最直观的感受。 同时,也为后续构建各类Spring Cloud组件和微服务应用做了一些基础准备工作。 另外, 我们对Spring Boot中的配置原理以及监控管理做了深入的介绍,因为这些内容将在后续的 介绍中有所涉及,并且它们有助于理解Spring Cloud组件的运行原理。