(译)Spring Cloud Config(第一部分)
原文链接:https://dzone.com/articles/spring-cloud-config-series-part-1-introduction
作者:Enrique Recarte
译者:umbrellage
本系列的 kickoff 讨论了如何使用 Spring Cloud 配置在您的环境中存储配置,并遵循12个要素的应用程序设计。
我是 Spring Cloud 系列工具的忠实粉丝。我使用它们已经有一段时间了,既有专业能力(虽然很有限),也有个人能力。如果您以前没有使用过它,您应该尝试一下。您将看到建立一个微服务环境是多么方便,在这个环境中,您的应用程序可以遵循12个因素应用程序声明。
Spring Cloud Config 简介
在环境中存储配置
这是声明的第三个因素。 在一个持续交付的世界里,管理我们的应用程序的配置变得更加重要,这样我们就可以从部署我们的应用程序中改变配置。 因为你希望能够尽快地对某些事件做出反应。 例如,更改HTTP调用的超时不应该意味着需要部署应用程序。如果您发现您的环境有一些临时问题,那么您应该可以很快地完成它。
Spring Cloud 遵循第三个因素的解决方案是Spring Cloud Config, 这基本上是一种独立于应用程序本身管理应用程序配置的方法。
在这篇文章中,我将介绍 Spring Cloud Config 技术,解释 Spring 如何在后台管理配置,如何在应用程序中使用该配置,最后介绍 Spring Cloud Config 里添加了什么。
理解 Spring 的配置文件资源
在每个 Spring 应用程序中,运行的配置文件总是以相同的方式处理。 运行的配置文件被称为属性源,基本上是一组按属性源来分组的配置文件,举个例子 ,将加载到应用程序中的 .properties
文件分组到一个 PropertySource
对象中。
当您的应用程序运行时,Spring 使用优先级属性源列表创建 Environment
,然后在每次您想要读取属性时,它将使用Spring 提供的查找配置的任何方式来查找属性(请参阅下一节)
为了获得更直观的表示,当一个非常简单的 Spring Boot 应用程序启动时,您可以看到以下 Environment
对象的屏幕截图:
如您所见,环境包含一个属性源列表。每当尝试在应用程序中使用属性时,Spring 都会按照属性值的顺序查询每个属性源。如果它在属性源中找到一个值,它将返回它。否则,它将转到下一个属性源。它将继续这样做,直到它在一个属性源中找到一个值,或者直到没有其他属性源为止。
对于上面的示例,在 application.yml 属性源中,如果您有一个名为 sample.firstProperty
的属性(在列表第七个里) ,Spring 将首先遍历前面的六个属性源。因为它们都不包含该值,所以最终会到达 application.yml
。但是,如果您给系统属性名为 sample.firstProperty
添加一些值,然后应用程序将使用该值,因为系统属性源是列表中的第四个元素。
现在我们已经总结了 Spring 的配置是如何构建的,让我们快速地看看如何从代码中使用该配置。
使用 Spring 的属性源
在代码中使用配置的主要方法有三种:
用 @Value 注释字段
@Component
@Data
public class AnnotatedProperties {
@Value("${sample.firstProperty:}")
private String firstProperty;
@Value("${sample.secondProperty:}")
private String secondProperty;
}
当应用程序启动时,Spring 提供的 bean 创建机制将检查这个类,并将提供的表达式的值注入到带注释的字段中。 这个表达式不必是属性名,也可以是 Spring 表达式。例如,您可以将逗号分隔的字符串转换为一个列表,其中包含:
@Value("#{'${my.list.of.strings}'.split(',')}")
注意这些表达式,因为它们不容易测试或阅读。
使用 @ConfigurationProperties
如果使用 Spring Boot,可以使用 @ConfigurationProperties
注释来使用类型安全配置:
@Component
@ConfigurationProperties(prefix = "sample")
@Data
public class ConfigProperties {
private String firstProperty;
private String secondProperty;
}
在本例中,Spring 将在属性源中查找所有以 sample
开头的属性。并将子属性与 @ConfigurationProperties
类中的属性名匹配。
这是对前面示例的改进,因为 Spring Boot 管理了许多类型转换。 它还提供了一个宽松的数据绑定,允许您以各种不同的方式声明属性, 如 sample.firstProperty
和 sample.first-property
。 您甚至可以将 JSR-303 注释(如 @NotNull
)添加到您的字段,Spring 将为您运行验证。
这种方法的另一个优点是,它鼓励您分层结构地构造属性,使它们更有组织。
查询 Environment
@Component
@RequiredArgsConstructor
public class EnvironmentProperties {
private final Environment environment;
public String getFirstProperty() {
return environment.getProperty("sample.firstProperty");
}
public String getSecondProperty() {
return environment.getProperty("sample.secondProperty");
}
}
最后一个选项是手工制作的。 您可以将 Environment
对象的一个实例写到您的组件中 并查询所需的特定属性。
Spring Cloud Config 配置
到目前为止,我们已经了解了 Spring 如何通过其属性源管理其配置,以及如何从组件中实际使用该配置。由于这篇文章是关于 Spring Cloud Config 的,让我们看看这张图带来了什么。
辅助程序属性源
在使用Spring Cloud Config 库时, 您的运行时配置设置将有轻微改变。如果我们用类路径中可用的这些库之一启动应用程序,比如Spring Cloud Config Zookeeper,检查应用程序的属性源,就像我们之前对普通应用程序所做的那样,你会注意到有一些不同之处。正如您在下图中看到的,还有一些属性源
(突出显示):
这些是 Spring Cloud 添加的一些属性源。最重要的是列表中的第二个元素,名为 bootstrapProperties
。Spring Cloud 引入了 PropertySourceLocator
的概念,该概念用于定位远程属性源。当您的应用程序启动时,这些远程属性源将被解析,然后将它们组合成一个 CompositePropertySource
,并将其插入到环境的优先级列表的顶部,并使用名称 bootstrapProperties
。这基本上意味着远程配置将覆盖您在应用程序中嵌入的任何配置,甚至是系统属性。
动态配置后端集成
我们刚刚描述了一个 PropertySourceLocator
对象如何负责加载远程属性。Spring Cloud Config 的一个主要特性是,它提供了一些不同的选项,用于从盒子中加载远程属性,如Git、Zookeeper 或 Consul。
需要注意的是,当您编写应用程序时,您可以选择如何使用前面讨论过的方法之一将配置注入到组件中,但是正如你所看到的,这些方法都没有定义这些属性的真正来源。代码完全不知道属性在哪里定义。这使得修改用于加载远程配置的底层技术变得非常容易,我们将在后续文章中看到这一点。
动态配置更新
我们之前提到过,当应用程序启动时,您的远程属性源就会被加载。由于 Spring Cloud Config 的整个目的是能够管理配置,而无需重新部署应用程序,它还提供了一种刷新配置的方法。
RefreshScope
Spring Cloud 为定义称为 RefreshScope
的 bean 提供了一个新的范围。如果您声明一个具有此范围的 @Bean
,那么 Spring Cloud 将把该 bean 封装在一个代理类中,这是其他组件实际要注入的。这个代理将把组件的每个方法代理到真正的实现中。
当应用程序中有刷新事件时 ,所有 RefreshScope
代理 bean 都将其底层 bean (真正的实现)标记为脏的。这意味着无论何时调用代理的任何方法,它都会首先重新创建底层bean,然后转发方法调用。这个bean的重新创建意味着它将再次读取配置。务必确保 @RefreshScopebean
是轻量级的,因为刷新事件将触发这些 bean 的重新构建。
让我们看得更详细一点。考虑以下 Spring 组件:
@RefreshScope
@Component
@Data
public class AnnotatedProperties {
@Value("${sample.firstProperty:}")
private String firstProperty;
@Value("${sample.secondProperty:}")
private String secondProperty;
}
@Service
@RequiredArgsConstructor
public class SampleService {
private final AnnotatedProperties annotatedProperties;
public String someOperation() {
return annotatedProperties.getFirstProperty();
}
}
下面的图描述了当刷新事件发生时的交互情况:
触发刷新事件
在查看了在 Spring Cloud 应用程序中发生刷新事件时发生的情况之后,让我们看看可以通过哪些方式触发此事件。
/refresh 执行器端点
如果您不知道 Spring Boot 的执行器端点是什么,您应该查看一下。这是 Spring Boot 最好的特性之一。这些端点有助于应用程序的操作,比如检查应用程序的健康状况,查看触发了什么自动配置等等。
其中一个端点可以通过发送 POST/refresh
HTTP 请求来触发上下文的刷新。
使用 Spring Cloud Bus
如果使用 Spring Cloud Bus 在应用程序之间进行通信,可以通过发送 RefreshRemoteApplicationEvent
来强制刷新应用程序。如果您的应用程序与 Spring Cloud Bus 集成,那么它们将有一个监听器用于该事件,该监听器将在接收该消息时触发刷新。这是 spring-cloud-config-monitor 库使用的方法。
触发刷新时的 Bean 行为
我认为很重要的一点是,当为前面描述的使用配置的不同方式触发刷新时,要了解bean的行为:
使用
@Value
注释的配置类不会在有刷新事件时自动刷新。出于这个原因,如果您使用这种方法,您将需要向组件添加@RefreshScope
,以便它获得刷新配置。@ConfigurationProperties
类将总是自动更新。不需要为这些使用@RefreshScope
。如果您查询环境,它将始终获得最新的配置,因此这种方法也不需要任何东西。
结论
我们已经了解了配置 Spring Cloud 背后的概念。我们已经看到了如何使用 Spring Cloud Config 将配置移出应用程序,我们还简要概述了如何在知道配置发生变化时触发配置刷新。
到目前为止,我们还没有过多地讨论具体的实现,这正是我们在后续文章中要做的。注意下一篇关于使用 Git 作为配置后端的文章。