第3章 使用Spring Cloud配置服务器控制配置

    本章主要内容
    将服务配置与服务代码分开
    配置Spring Cloud配置服务器
    集成Spring Boot微服务
    加密敏感属性
    在某种程度上来说,开发人员是被迫将配置信息与他们的代码分开的。毕竟,自上学以来,他们就一直被灌输不要将硬编码带入应用程序代码中的观念。许多开发人员在应用程序中使用一个常量类文件来帮助将所有配置集中在一个地方。将应用程序配置数据直接写入代码中通常是有问题的,因为每次对配置进行更改时,应用程序都必须重新编译和重新部署。为了避免这种情况,开发人员会将配置信息与应用程序代码完全分离。这样就可以很容易地在不进行重新编译的情况下对配置进行更改,但这样做也会引入复杂性,因为现在存在另一个需要与应用程序一起管理和部署的制品。
    许多开发人员转向低层级的属性文件(即YAML、JSON或XML)来存储他们的配置信息。这份属性文件存放在服务器上,通常包含数据库和中间件连接信息以及驱动应用程序行为的相关元数据。将应用程序分离到一个属性文件中是很简单的,除了将配置文件放在源代码控制下(如果需要这样做的话),并将配置文件部署为应用程序的一部分,大多数开发人员永远不会再对应用程序配置进行实施。
    这种方法可能适用于少量的应用程序,但是在处理可能包含数百个微服务的基于云的应用程序,其中每个微服务可能会运行多个服务实例时,它会迅速崩溃。
    配置管理突然间变成一件重大的事情,因为在基于云的环境中,应用程序和运维团队必须与配置文件的“鼠巢”进行斗争。基于云的微服务开发强调以下几点。
(1)应用程序的配置与正在部署的实际代码完全分离。
    (2)构建服务器、应用程序以及一个不可变的镜像,它们在各环境中进行提升时永远不会发生变化。
    (3)在服务器启动时通过环境变量注入应用程序配置信息,或者在微服务启动时通过集中式存储库读取应用程序配置信息。
    本章将介绍在基于云的微服务应用程序中管理应用程序配置数据所需的核心原则和模式。
3.1 管理配置(和复杂性)
    对于在云中运行的微服务,管理应用程序配置是至关重要的,因为微服务实例需要以最少的人为干预快速启动。每当人们需要手动配置或接触服务以实现部署时,都有可能出现配置漂移、意外中断以及应用程序响应可伸缩性挑战出现延迟的情况。
    通过建立要遵循的4条原则,我们来开始有关应用程序配置管理的讨论。
    (1)分离——我们希望将服务配置信息与服务的实际物理部署完全分开。应用程序配置不应与服务实例一起部署。相反,配置信息应该作为环境变量传递给正在启动的服务,或者在服务启动时从集中式存储库中读取。
    (2)抽象——将访问配置数据的功能抽象到一个服务接口中。应用程序使用基于REST的JSON服务来检索配置数据,而不是编写直接访问服务存储库的代码(也就是从文件或使用JDBC从数据库读取数据)。
    (3)集中——因为基于云的应用程序可能会有数百个服务,所以最小化用于保存配置信息的不同存储库的数量至关重要。将应用程序配置集中在尽可能少的存储库中。
    (4)稳定——因为应用程序的配置信息与部署的服务完全隔离并集中存放,所以不管采用何种方案实现,至关重要的一点就是保证其高可用和冗余。
    要记住一个关键点,将配置信息与实际代码分开之后,开发人员将创建一个需要进行管理和版本控制的外部依赖项。我总是强调应用程序配置数据需要跟踪和版本控制,因为管理不当的应用程序配置很容易滋生难以检测的bug和计划外的中断。
3.1.1 配置管理架构
从第2章中可以看出,微服务配置管理的加载发生在微服务的引导阶段。作为回顾,图3-1展示了微服务生命周期。
 
 
图3-1 应用程序配置数据在服务引导阶段被读取
    我们先来看一下之前在3.1节中提到的4条原则(分离、抽象、集中和稳定),看看这4条原则在服务引导时是如何应用的。图3-2更详细地探讨了引导过程,并展示了配置服务在此步骤中扮演的关键角色。
 
 
在图3-2中,发生了以下几件事情。
    (1)当一个微服务实例出现时,它将调用一个服务端点来读取其所在环境的特定配置信息。配置管理的连接信息(连接凭据、服务端点等)将在微服务启动时被传递给微服务。
    (2)实际的配置信息驻留在存储库
中。基于配置存储库的实现,可以选择使用不同的实现来保存配置数据。配置存储库的实现选择可以包括源代码控制下的文件、关系数据库或键值数据存储。
    (3)应用程序配置数据的实际管理与应用程序的部署方式无关。配置管理的更改通常通过构建和部署管道来处理,其中配置的更改可以通过版本信息进行标记,并通过不同的环境进行部署。
    (4)进行配置管理更改时,必须通知使用该应用程序配置数据的服务,并刷新应用程序数据的副本。
    现在,我们已经完成了概念架构,这个概念架构阐示了配置管理模式的各个组成部分,以及这些部分如何组合在一起。
我们现在要继续看看这些模式的不同解决方案,然后看一下具体的实现。
    3.1.2 实施选择
    幸运的是,开发人员可以在大量久经测试的开源项目中进行选择,以实施配置管理解决方案。我们来看一下几个不同的方案选择,并对它们进行比较。表3-1列出了这些方案选择。
    表3-1 用于实施配置管理系统的开源项目
    
 
项目名称
 
描述
 
特性
 
Etcd
使用Go开发的开源项目,用于服务发现和键值管理,使用raft协议作为它的分布式计算模型
非常快和可伸缩 
可分布式 
命令行驱动 
易于搭建和使用
Eureka
由Netflix开发。久经测试,用于服务发现和键值管理
分布式键值存储 
灵活,
需要费些功夫去设置 
提供开箱即用的动态客户端刷新
Consul
 
由Hashicorp开发,特性上类似于Etcd和Eureka,它的分布式计算模型使用了不同的算法(SWIM协议)
快速 
提供本地服务发现功能,可直接与DNS集成 
没有提供开箱即用的动态客户端刷新
ZooKeeper
 
一个提供分布式锁定功能的Apache项目,经常用作访问键值数据的配置管理解决方案
最古老的、最久经测试的解决方案
使用最为复杂 
可用作配置管理,
但只有在其他架构中已经使用了ZooKeeper的时候才考虑使用它
Spring Cloud
server
 
一个开源项目,提供不同后端支持的通用配置管理解决方案。它可以将Git、Eureka和Consul作为后端进行整合
非分布式键值存储 
提供了对Spring和非Spring服务的紧密集成 
可以使用多个后端来存储配置数据,
包括共享文件系统、Eureka、Consul和Git
表3-1中的所有方案都可以轻松用于构建配置管理解决方案。对于本章和本书其余部分的示例,都将使用Spring Cloud配置服务器。选择这个解决方案出于多种原因,其中包括以下几个。
    (1)Spring Cloud配置服务器易于搭建和使用。
    (2)Spring Cloud配置与Spring Boot紧密集成。开发人员可以使用一些简单易用的注解来读取所有应用程序的配置数据。
    (3)Spring Cloud配置服务器提供多个后端用于存储配置数据。如果读者已经使用了Eureka和Consul等工具,那么可以将它们直接插入Spring Cloud配置服务器中。
    (4)在表3-1所示的所有解决方案中,Spring Cloud配置服务器可以直接与Git源控制平台集成。Spring Cloud配置与Git的集成消除了解决方案的额外依赖,并使版本化应用程序配置数据成为可能。
    其他工具(Etcd、Consul、Eureka)不提供任何类型的原生版本控制,如果开发人员想要版本控制的话,则必须自己去建立它。如果读者使用Git,那么使用Spring Cloud配置服务器是一个很有吸引力的选择。
    对于本章的其余部分,我们将要完成以下几项工作。
    (1)创建一个Spring Cloud配置服务器,并演示两种不同的机制来提供应用程序配置数据,一种使用文件系统,另一种使用Git存储库。
    (2)继续构建许可证服务以从数据库中检索数据。
    (3)将Spring Cloud配置服务挂钩(hook)到许可证服务,以提供应用程序配置数据。
    3.2 构建Spring Cloud配置服务器
    Spring Cloud配置服务器是基于REST的应用程序,它建立在Spring Boot之上。Spring Cloud配置服务器不是独立服务器,相反,开发人员可以选择将它嵌入现有的Spring Boot应用程序中,也可以在嵌入它的服务器中启动新的Spring Boot项目。
    首先需要做的是建立一个名为confsvr的新项目目录。在confsvr目录中创建一个新的Maven文件,该文件将用于拉取启动Spring Cloud配置服务器所需的JAR文件。代码清单3-1列出的是关键部分,而不是整个Maven文件。
    代码清单3-1 为Spring Cloud配置服务器创建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.thoughtmechanix</groupId>
 
<artifactId>configurationserver</artifactId>   
<version>0.0.1-SNAPSHOT</version>   
<packaging>jar</packaging>  
 <name>Config Server</name>   
<description>
Config Server demo project
</description>   
<parent>     
<groupId>org.springframework.boot</groupId>     
<artifactId>spring-boot-starter-parent</artifactId>     
<version>1.4.4.RELEASE</version>    ⇽---① 将要使用的Spring Boot版本   
</parent>  
 <dependencyManagement>     
<dependencies>       
<dependency>         
<groupId>org.springframework.cloud</groupId>         
<artifactId>
spring-cloud-dependencies
</artifactId>        
 <version>Camden.SR5</version>    ⇽--- ②将要使用的Spring Cloud版本         
<type>pom</type>         
<scope>import</scope>     
  </dependency>
</dependencies>
</dependencyManagement>
 
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>com.thoughtmechanix.confsvr.ConfigServerApplication </start-class> ←③用于配置服务器的引导类
<java.version>1.8</java.version>
<docker.image.name>johncarnell/tmx-confsvr</docker.image.name>
<docker.image.tag>chapter3</docker.image.tag>
</properties>
 
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>    ←④你将在这个特定服务中使用的Spring Cloud项目
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>    ←④你将在这个特定服务中使用的Spring Cloud项目
</dependency>
</dependencies>
<!-- 未显示Docker构建配置 -->
</project>
 
在代码清单3-1所示的Maven文件中,首先声明了要用于微服务的Spring Boot版本(1.4.4版本)。下一个重要的Maven定义部分是将要使用的Spring Cloud Config父物料清单(Bill of Materials,BOM)。Spring Cloud是一个大量独立项目的集合,这些项目全部遵循自身的发行版本而更新。此父BOM包含云项目中使用的所有第三方库和依赖项以及构成该版本的各个项目的版本号。在这个例子中,我们使用Spring Cloud的Camden.SR5版本。通过使用BOM定义,可以保证在Spring Cloud中使用子项目的兼容版本。这也意味着不必为子依赖项声明版本号。代码清单3-1的剩余部分负责声明将在服务中使用的特定Spring Cloud依赖项。第一个依赖项是所有Spring Cloud项目使用的spring-cloud-starter-config。第二个依赖项是spring- cloud-config-server起步项目,它包含了spring-cloud-config-server的核心库。
    来吧,坐上发行版系列的列车
    Spring Cloud使用非传统机制来标记Maven项目。Spring Cloud是独立子项目的集合。Spring Cloud团队通过其称为“发行版系列”(release train)的方式进行版本发布。组成Spring Cloud的所有子项目都包含在一个Maven物料清单(BOM)中,并作为一个整体进行发布。Spring Cloud团队一直使用伦敦地铁站的名称作为他们发行版本的名称,每个递增的主要版本都按字母表从小到大的顺序赋予一个伦敦地铁站的站名。目前已有3个版本,即Angel、Brixton和Camden。Camden是迄今为止最新的发行版,但是它的子项目中仍然有多个候选版本分支。
 
需要注意的是,Spring Boot是独立于Spring Cloud发行版系列发布的。因此,Spring Boot的不同版本可能与Spring Cloud的不同版本不兼容。参考Spring Cloud网站,可以看到Spring Boot和Spring Cloud之间的版本依赖项,以及发行版系列中包含的不同子项目版本。
    我们仍然需要再多创建一个文件来让核心配置服务器正常运行。这个文件是位于confsvr/src/ main/resources目录中的application.yml文件。application.yml文件告诉Spring Cloud配置服务要侦听哪个端口以及在哪里可以找到提供配置数据的后端。
    我们马上就能启动Spring Cloud配置服务了。现在,需要将服务器指向保存配置数据的后端存储库。对于本章,读者将要使用第2章中开始构建的许可证服务作为使用Spring Cloud Config的示例。简单起见,我们将为以下3个环境创建配置数据:在本地运行服务时的默认环境、开发环境以及生产环境。
    在Spring Cloud配置中,一切都是按照层次结构进行的。应用程序配置由应用程序的名称表示。我们为需要拥有配置信息的每个环境提供一个属性文件。在这些环境中,我们将创建两个配置属性:
 
    n 由许可证服务直接使用的示例属性;
    n 用于存储许可证服务数据的Postgres数据库的配置。
    图3-3阐述了如何创建和使用Spring Cloud配置服务。需要注意的是,在构建配置服务时,它将成为在环境中运行的另一个微服务。一旦建立配置服务,服务的内容就可以通过基于HTTP的REST端点进行访问。
 
 
图3-3 Spring Cloud配置将环境特定的属性公开为基于HTTP的端点
    应用程序配置文件的命名约定是“应用程序名称-环境名称.yml”。从图3-3可以看出,环境名称直接转换为可以浏览配置信息的 URL。随后,启动许可证微服务示例时,要运行哪个服务环境是由在命令行服务启动时传入的 Spring Boot 的 profile 指定的。如果在命令行上没有传入profile,Spring Boot将始终默认加载随应用程序打包的application.yml文件中的配置数据。
    以下是为许可证服务提供的一些应用程序配置数据的示例。这些数据包含在 confsvr/src/ main/resources/con-fig/licensingservice/licensingservice.yml文件中,图3-3引用了这些数据。下面是此文件的一部分内容:
tracer.property: "I AM THE DEFAULT" 
spring.jpa.database: "POSTGRESQL" 
spring.datasource.platform:  "postgres" 
spring.jpa.show-sql: "true" 
spring.database.driverClassName: "org.postgresql.Driver" 
spring.datasource.url: "jdbc:postgresql://database:5432/eagle_eye_local
 
spring.datasource.username: "postgres" 
spring.datasource.password: "p0stgr@s" 
spring.datasource.testWhileIdle: "true" 
spring.datasource.validationQuery: "SELECT 1" 
spring.jpa.properties.hibernate.dialect: ➥  "org.hibernate.dialect.PostgreSQLDialect"
    在实施前想一想
我建议不要在中大型云应用中使用基于文件系统的解决方案。使用文件系统方法,意味着要为想要访问应用程序配置数据的所有云配置服务器实现共享文件挂载点。在云中创建共享文件系统服务器是可行的,但它将维护此环境的责任放在开发人员身上。
    我将展示如何以文件系统作为入门使用Spring Cloud配置服务器的最简单示例。在后面的几节中,我将介绍如何配置Spring Cloud配置服务器以使用基于云的Git供应商(如Bitbucket或GitHub)来存储应用程序配置。
 
3.2.1 创建Spring Cloud Config引导类
    本书涵盖的每一个Spring Cloud服务都需要一个用于启动该服务的引导类。这个引导类包含两样东西:作为服务启动入口点的Java main()方法,以及一组告诉启动的服务将要启动哪种类型的Spring Cloud行为的Spring Cloud注解。
    代码清单3-2展示了用作配置服务的引导类Application(在 confsvr/src/main/java/com/ thoughtmechanix/confsvr/Application.java中)。
 
    代码清单3-2 Spring Cloud Config服务器的引导类
 
package com.thoughtmechanix.confsvr; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.cloud.config.server.EnableConfigServer; 
@SpringBootApplication   ← Spring Cloud Config服务是Spring Boot应用程序,因此需要用@SpringBoot Application进行标记 @EnableConfigServer        ←@EnableConfigServer使服务成为Spring Cloud Config服务 
public class ConfigServerApplication {     
public static void main(String[] args) {    ←main方法启动服务并启动Spring容器         SpringApplication.run(ConfigServerApplication.class, args);    
 } 
}
    接下来,我们将使用最简单的文件系统示例来搭建Spring Cloud配置服务器。
    3.2.2 使用带有文件系统的Spring Cloud配置服务器
    Spring Cloud配置服务器使用confsvr/src/main/resources/application.yml文件中的条目指向要保存应用程序配置数据的存储库。
创建基于文件系统的存储库是实现这一目标的最简单方法。
    为此,要将以下信息添加到配置服务器的application.yml文件中。
代码清单3-3展示Spring Cloud配置服务器的application.yml文件的内容。
    代码清单3-3 Spring Cloud配置的application.yml文件
    server:  
         port: 8888    ⇽---① Spring Cloud配置服务器将要监听的端口 
    spring:   
       profiles:     
            active: native    ⇽---②  用于存储配置  
        cloud:     
            config:      
                 server:         
                    native:           
                        searchLocations: file:///Users/johncarnell1/book/    ⇽---  配置文件存储位置的路径
                        native_cloud_apps/ch4-config-managment/confsvr/src/main/
                        resources/config/licensingservice
 
 
    在代码清单3-3所示的配置文件中,首先告诉配置服务器,对于所有配置信息的请求,应该监听哪个端口号:
    server:   port: 8888
    因为我们正在使用文件系统来存储应用程序配置信息,所以需要告诉Spring Cloud配置服务器以“native”profile运行:
profiles:   active: native
    application.yml文件的最后一部分为Spring Cloud配置提供了应用程序数据所在的文件目录:
 
server:
    native:
code/confsvr/src/main/resources/config
 
 
配置条目中的重要参数是searchLocations属性。这个属性为每一个应用程序提供了用逗号分隔的文件夹列表,这些文件夹含有由配置服务器管理的属性。在上一个示例中,只配置了许可证服务。
    注意
如果使用Spring Cloud Config的本地文件系统版本,那么在本地运行代码时,需要修改spring.cloud.config.server.native.searchLocations属性以反映本地文件路径。
    我们现在已经完成了足够多的工作来启动配置服务器。接下来,我们就使用 mvn spring-boot:run命令启动配置服务器。服务器现在应该在命令行上出现一个Spring Boot启动画面。如果用浏览器访问http://localhost:8888/licensingservice/default,那么将会看到JSON净荷与licensingservice.yml文件中包含的所有属性一起返回。
 
图3-4展示了调用此端点的结果图3-4 检索许可证服务的默认配置信息
 
 
    如果读者想要查看基于开发环境的许可证服务的配置信息,可以对http://localhost: 8888/licensingservice/dev端点发起GET请求。图3-5展示了调用此端点的结果。
 
 
图3-5 使用开发环境profile检索许可证服务的配置信息
 
如果仔细观察,读者会看到在访问开发环境端点时,将返回许可证服务的默认配置属性以及开发环境下的许可证服务配置。Spring Cloud配置返回两组配置信息的原因是,Spring框架实现了一种用于解析属性的层次结构机制。当Spring框架执行属性解析时,它将始终先查找默认属性中的属性,然后用特定环境的值(如果存在)去覆盖默认属性。
    具体来说,如果在licensingservice.yml文件中定义一个属性,并且不在任何其他环境配置文件(如licensingservice-dev.yml)中定义它,则Spring框架将使用这个默认值。
 
注意
    
    这不是直接调用Spring Cloud配置REST端点所看到的行为。REST端点将返回调用的默认值和环境特定值的所有配置值。
    让我们看看如何将Spring Cloud配置服务器挂钩到许可证微服务。
 
posted @ 2019-12-02 20:46  mongotea  阅读(408)  评论(0编辑  收藏  举报