Spring Cloud Config(第1部分)

Spring Cloud Config (Part 1)

我是Spring Cloud工具集的忠实拥护者。 我已经以一种专业的身份(尽管相当有限)和个人身份使用了一段时间。 如果您以前从未使用过,则应尝试一下。 您会看到设置微服务环境的便利性,使您的应用程序可以遵循《十二因素应用宣言》。

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对象的屏幕截图,如下所示:

Environment Snapshot for Spring Boot Application

如您所见,该环境包含属性来源列表。 每当您尝试在应用程序中使用属性时,Spring都会开始按顺序查询每个属性源以获取属性的值。 如果在该属性源中找到一个值,则将其返回。 否则,它将转到下一个属性源。 它将继续执行此操作,直到在"属性来源"之一中找到值,或者直到没有"属性来源"为止。

对于上面的示例,如果您在application.yml属性源(列表中的数字7)中有一个名为sample.firstProperty的属性,Spring将首先浏览前面的六个属性源。 由于它们都不包含该值,它将最终达到application.yml。 但是,如果您添加一个名为sample.firstProperty的系统属性并带有某个值,则该应用程序将使用该值,因为系统属性源是列表中的第四个元素。

现在,我们已经概述了Spring的配置的结构,让我们快速看一下如何从代码中使用该配置。

使用Spring的属性源

有三种在代码中使用配置的主要方法:

@Value注释字段

1
2
3
4
5
6
7
8
@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注释来使用类型安全的配置:

1
2
3
4
5
6
7
@Component
@ConfigurationProperties(prefix ="sample")
@Data
public class ConfigProperties {
    private String firstProperty;
    private String secondProperty;
}

在这种情况下,Spring将在属性源中查找以sample.开头的所有属性,并将子属性与@ConfigurationProperties类中的属性名称匹配。

这是对上一个示例的改进,因为Spring Boot管理许多类型转换。 它还提供了轻松的数据绑定,使您可以用各种不同的方式声明属性,例如sample.firstPropertysample.first-property。 您甚至可以在字段中添加@NotNull之类的JSR-303注释,Spring会为您运行验证。

这种方法的另一个优点是,它鼓励您按层次结构构造属性,使它们更有条理。

查询Environment

1
2
3
4
5
6
7
8
9
10
11
12
13
@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如何通过其Property Sources管理其配置,以及如何从组件中实际使用该配置。 既然这篇文章是关于Spring Cloud Config的,那么让我们看看它带来了什么。

Bootstrap属性源

使用Spring Cloud Config库之一时,您的运行时配置设置将略有变化。 如果我们使用类路径中可用的这些库之一(例如Spring Cloud Config Zookeeper)启动我们的应用程序,并且像我们之前对普通应用程序所做的那样检查应用程序的属性源,您会发现存在一些差异。 如您在下图中所看到的,还有更多的属性来源(突出显示):

Environment Snapshot for a Spring Cloud Config Application

这些是Spring Cloud添加的一些资源来源。 最重要的一个是列表中名称为bootstrapProperties的第二个元素。 Spring Cloud引入了PropertySourceLocator的概念,用于定位远程属性源。 这些远程属性源会在您的应用程序启动之前立即进行解析,然后再执行其他任何操作,然后将它们组合成一个CompositePropertySource,该CompositePropertySource插入名称为bootstrapProperties的环境优先级列表的顶部。 基本上,这意味着远程配置将覆盖您已嵌入到应用程序甚至系统属性中的任何配置。

动态配置后端集成

我们刚刚描述了PropertySourceLocator对象如何负责加载远程属性。 Spring Cloud Config的主要功能之一是,它提供了几种不同的选项,可以立即加载远程属性,例如Git,Zookeeper或Consul。

重要的是要注意,在编写应用程序代码时,可以使用前面讨论的方法之一来选择如何将配置注入组件中,但是正如您所看到的那样,这些方法都没有定义这些属性实际到达的位置 从。 该代码完全不知道属性的定义位置。 这使得更改用于加载远程配置的基础技术非常容易,正如我们将在后续文章中看到的那样。

动态配置刷新

前面我们提到过,您的远程属性源是在应用程序启动时立即加载的。 由于Spring Cloud Config的全部目的是能够管理配置而不必重新部署您的应用程序,因此它还提供了一种刷新配置的方法。

RefreshScope

Spring Cloud提供了一个用于定义Bean的新范围,称为RefreshScope。 如果您声明具有此范围的@Bean,Spring Cloud会将那个bean包装在一个代理类中,这实际上是注入其他组件的方式。 该代理只会将组件的每种方法代理到实际的实现中。

每当应用程序中发生刷新事件时,所有RefreshScope代理bean都会将其基础bean(实际实现)标记为脏。 这意味着无论何时调用代理的任何方法,它都将首先重新创建基础bean,然后再转发方法调用。 重新创建Bean意味着它将再次读取配置。 确保@RefreshScope bean是轻量级的很重要,因为刷新事件将触发这些bean的重构。

让我们更详细地看一下。 考虑以下Spring组件:

1
2
3
4
5
6
7
8
9
@RefreshScope
@Component
@Data
public class AnnotatedProperties {
    @Value("${sample.firstProperty:}")
    private String firstProperty;
    @Value("${sample.secondProperty:}")
    private String secondProperty;
}

1
2
3
4
5
6
7
8
9
@Service
@RequiredArgsConstructor
public class SampleService {
    private final AnnotatedProperties annotatedProperties;

    public String someOperation() {
        return annotatedProperties.getFirstProperty();
    }
}

下图描述了刷新事件发生时此交互的外观:

Refresh Event Sequence Diagram

触发刷新事件

在查看了Spring Cloud应用程序中的刷新事件时发生了什么之后,让我们看一下触发该事件的不同方式。

/refresh执行器端点

如果您不知道什么是Spring Boot的Actuator Endpoints,则应该看看。 这是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 Config背后的概念。 我们已经了解了如何使用Spring Cloud Config将配置移出应用程序,并且还简要了解了如何在知道更改后触发刷新配置的方法。

    到目前为止,我们还没有真正讨论具体的实现,而这正是我们在后续文章中将要做的。 寻找有关使用Git作为配置后端的下一篇文章。