一、为什么需要将服务配置与服务代码分开?
在应用开发的过程中,许多开发人员会在应用程序中使用一个或多个常量类文件来将所有的配置集中在一个地方。将应用程序配置数据直接写入代码中通常是有问题的,因为每次对配置镜像更改时,应用程序都必须重新编译和重新部署。而使用配置文件将配置信息与应用程序代码进行分离,这样可以很容易在不进行重新编译的情况下对配置进行更改,且修改后重启应用即可使修改后的配置生效。
在开发时往往会采用YAML、JSON、XML或Properties等文件来存储应用的配置信息。这些属性文件存放在服务器上,通常包含数据库和中间价信息,以及驱动应用程序行为的相关元数据。通常我们会将配置文件放在源码控制器下,并将配置文件部署为应用程序的一部分。这种方法可能适用于少量的应用程序。但是在处理可能包含数百个微服务的基于云的应用程序时,其中每个微服务可能会运行多个服务实例。基于这样的场景,你会发现管理每个应用的配置突然就变成一件繁琐且重要的事情。将配置文件和应用程序部署的方案将迅速崩溃。
基于微服务开发和K8S(或云)部署时,需要强调以下几点:
(1)应用程序的配置与正在部署的实际代码完全分离。
(2)构建服务器、应用程序以及一个不可变的镜像,它们在各环境中进行升级时永远不会发生变化。
(3)在服务启动时通过环境变量注入应用程序的配置信息,或者在微服务启动时通过集中式存储库读取应用程序配置信息。
二、配置管理
对于在K8S(或云)中运行的微服务,管理应用程序配置是至关重要的,因为微服务实例需要以最少的人为干预快速启动。每当需要人为手动配置或者接触服务已实现部署时,都有可能出现错误。
建立应用程序配置管理需要遵循的4条原则。
(1)分离——我们希望将配置信息与服务的实际物理部署完全分开。应用程序配置不应该与服务实例一起部署。相反,配置信息应该作为环境变量传递给正在启动的服务,或者在服务启动时从集中式存储库中读取。
(2)抽象——将访问配置数据的功能抽象到一个服务接口中。应用程序使用基于REST的JSON服务来检索配置数据,而不是编写直接访问服务存储库的代码(也就是从文件或使用JDBC从数据库读取数据)。
(3)集中——因为基于K8S(或云)部署的应用程序可能会有数百个甚至更多的服务,所以最小化用于保存配置信息的不同存储库的数量至关重要。将应用程序配置集中在尽可能少的存储库中。
(4)稳定——因为应用程序的配置信息与部署的服务完全隔离并集中存放,所以不管采取用何种方案实现,至关重要的一点就是保证其高可用和冗余。
要记住一个关键点,将配置信息与实际代码分开后,开发人员将创建一个需要进行管理和版本控制的外部依赖项(比如使用Git)。因为管理不当的应用程序配置很容易滋生难以检测的BUG和计划外的中断。
三、构建Spring Cloud配置服务器
Spring Cloud配置服务器是基于REST的应用程序,它建立在Spring Boot之上。
首先我们先创建一个pom工程作为父工程。例如工程名称为microservice。其pom.xml信息如下(说明一下:当前的Spring Cloud配置服务只是使用部分依赖。其他依赖后续使用)。
<?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>org.jackson</groupId>
<artifactId>mircoservice</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>confsvr</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.boot.version>2.1.13.RELEASE</spring.boot.version>
<spring.cloud.version>Greenwich.SR6</spring.cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!-- 由于Spring Cloud高版本使用Gateway替换Zuul,所以需要在此声明引入zuul的版本信息 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>