Spring Cloud Config配置服务及那些你不知道的坑

news2025/2/23 19:44:31

目录

1、为什么选择Spring Cloud Config

1.1 集中式管理

1.2 动态修改配置

2、Spring Cloud Config 简介

3、服务端配置

3.1 添加依赖

3.2 开启服务注册

3.3 添加YML配置

3.4 创建远程分支及Profile配置文件

3.5 启动并测试服务

4、客户端配置

4.1 添加依赖

4.2 开启服务注册

4.3 添加YML配置

4.4 启动并测试服务

5、原因分析

5.1 定位问题原因

5.2 定位问题2分析过程

5.3 定位问题1分析过程

5.3.1 逆向排查

5.3.2 正向跟踪


1、为什么选择Spring Cloud Config

如何将配置信息直接写在本地yml配置文件存在哪些痛点呢?

如果多个微服务可能使用相同的配置信息,假设有50个微服务,如果配置需要修改配置文件,就意味着我们需要修改50个微服务的yml文件,极其浪费时间。配置信息修改后,必须重启服务才能生效。

相比较同类产品,SpringCloudConfig最大的优势是和Spring无缝集成,支持Spring里面Environment和PropertySource的接口,对于已有的Spring应用程序的迁移成本非常低,在配置获取的接口上是完全一致,结合SpringBoot可使你的项目有更加统一的标准(包括依赖版本和约束规范),避免了应为集成不同开软件源造成的依赖版本冲突。

而Spring Cloud Config解决了这两个痛点:

1.1 集中式管理

在开发中多个微服务可能使用相同的配置,假设有50个微服务,如果配置需要修改配置文件,就意味着我们需要修改50个微服务的yml文件。使用配置中心后,就可以做到一处修改,处处修改。

1.2 动态修改配置

使用配置中心,配合actuator可以实现配置的动态修改,无需重启服务

2、Spring Cloud Config 简介

SpringCloudConfig就是我们通常意义上的配置中心,把应用原本放在本地文件的配置抽取出来放在中心服务器,从而能够提供更好的管理、发布能力。SpringCloudConfig分服务端和客户端,服务端负责将git svn中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置,这需要每个客户端通过POST方法触发各自的/refresh。

SpringCloudBus通过一个轻量级消息代理连接分布式系统的节点。这可以用于广播状态更改(如配置更改)或其他管理指令。SpringCloudBus提供了通过POST方法访问的endpoint/bus/refresh,这个接口通常由git的钩子功能调用,用以通知各个SpringCloudConfig的客户端去服务端更新配置。

注意:这是工作的流程图,实际的部署中SpringCloudBus并不是一个独立存在的服务,这里单列出来是为了能清晰的显示出工作流程。

下图是SpringCloudConfig结合SpringCloudBus实现分布式配置的工作流:

 

简单说明一下流程:

1)把配置文件放在Git Repository中。

2)Config Server从Git repository中读取配置信息。

3)其他客户端再从Config Server中加载配置文件

3、服务端配置

紧接上一篇内容代码示例,创建一个新的module. 命名为:springcloud-config-server。

 

3.1 添加依赖

在springcloud-config-server pom.xml中添加config-server依赖。如下,

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-config-server</artifactId>

</dependency>

 

3.2 开启服务注册

新建启动类SpringCloudConfigServerApp,并添加@EnableConfigServer,表示开启 SpringCloudConfig配置。

代码示例如下,

package com.xintu.springcloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.config.server.EnableConfigServer;



/**

 * @author XinTu

 * @classname SpringCloudConfigServerApp

 * @description TODO

 * @date 2023年03月23日 14:02

 */



@SpringBootApplication

@EnableConfigServer

public class SpringCloudConfigServerApp {

    public static void main(String[] args) {

        SpringApplication.run(SpringCloudConfigServerApp.class, args);

    }

}

3.3 添加YML配置

新增配置文件 application.yml。配置文件添加内容如下,

# 服务端口

server:

  port: 8086



#指定应用名称

spring:

  application:

    name: config-server

  cloud:

    config:

      label: master #配置git仓库分支

      server:

        git:

          uri: https://gitee.com/lwbook/spring-cloud-config.git #配置git仓库地址

          search-paths: spring-cloud-config #配置仓库路径

          #username:  git_username #访问git仓库的用户名,公开仓库不配置用户名

          #password: git_password #访问git仓库的用户密码,公开仓库不配置密码

3.4 创建远程分支及Profile配置文件

1) 远程配置仓库及文件根据自己公司环境自行创建。

这里老王提供一个自己创建的公共Git地址。https://gitee.com/lwbook/spring-cloud-config.git。

该仓库下新建了一个xintu-config文件夹。

在xintu-config 文件夹下分别创建3个文件。

注意:文件命名规格:{项目名}-{配置环境版本}.yml。比如application-dev.yml,表示的是application项目的开发环境配置。

📕开发环境配置: application-dev.yml

添加内容:

env: dev

test: 1

📕测试环境配置:application-test.yml

添加内容:

env: test

test: 2

📕预发环境配置:application-pre.yml

添加内容:

env: pre

test: 3

 3.5 启动测试服务

启动程序 SpringCloudConfigServerApp类后,输入http://localhost:8086/application/dev访问 springcloud-config-server服务。出现如下界面,则表示配置服务中心可以从远程程序获取配置信息。

 

{"name":"application","profiles":["dev"],"label":null,"version":"6abb9b9f47dedfe76592d3496a057dce4f74a9fe","state":null,"propertySources":[{"name":"https://gitee.com/lwbook/spring-cloud-config.git/xintu-config/application-dev.yml","source":{"env":"dev","test":1}}]}

看下后台日志打印,

 

至此,COnfig服务端配置完成。

4、客户端配置

创建一个新的module. 命名为:springcloud-config-client。

 

4.1 添加依赖

在springcloud-config-client pom.xml中添加config-client依赖。如下,

<!-- config client-->

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-config-client</artifactId>

</dependency>

<!-- 添加web 用于获取config数据测试 -->

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>

 

4.2 开启服务注册

新建启动类SpringCloudConfigClientApp, 代码示例如下,


package com.xintu.springcloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;



/**

 * @author XinTu

 * @classname SpringCloudConfigClientApp

 * @description TODO

 * @date 2023年03月23日 14:02

 */



@SpringBootApplication

public class SpringCloudConfigClientApp {

    public static void main(String[] args) {

        SpringApplication.run(SpringCloudConfigClientApp.class, args);

    }

}

新建一个测试controller类,在程序的启动类 ConfigClientController 通过 @Value 获取服务端的 env和 test值的内容。

代码示例:

package com.xintu.springcloud.controller;



import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;



/**

 * @author XinTu

 * @classname ConfigClientController

 * @description TODO

 * @date 2023年03月23日 14:20

 */

@RestController

public class ConfigClientController {

    @Value("${env}")

    String env;



    @Value("${test}")

    String test;



    @RequestMapping("/")

    public String test() {

        return String.format("env=%s;test=%s", env, test);

    }

}

4.3 添加YML配置

添加配置文件 application.yml。

# 服务注册中心 (单节点)

server:

  port: 8087



#指定应用名称

spring:

  application:

    name: config-client

  cloud:

    config:

      label: master #配置git仓库分支

      profile: dev #开发环境配置文件

      uri: http://localhost:8086/ #指明配置服务中心的网址



#配置默认值,否则启动时会报错;配置中心有则用配置中心

env: prod

test: 4

4.4 启动并测试服务

启动程序 SpringCloudConfigClientApp类后,发现如下异常信息。

 

1)看第一条错误信息,加载的地址端口不是8086,而是8888.

2)看第二条错误信息,虽然端口没变,但是application、profile和lable变成了我们自己配置的内容。

 

我们再访问页面:http://localhost:8087/test

 

* 期望的是env=dev,test=3.而返回的是prod和4.说明我们的git配置没获取成功。

先说解决方案:

方案一:把application.yml换为bootstrap.yml文件即可。

方案二:把config serverz的端口改为8888,当然这种只限于localhost模式,跨服务器是行不通的。

问题原因分析见第5节。

5、原因分析

网上有类似的问题的回答,建议把application.yml调整为bootstrap.yml,修改后确实生效了。但这个是真的的答案吗?能解释问题1,但是无法解释问题2。所以,我们还需要继续查找问题根因。

先看官网:https://cloud.spring.io/spring-cloud-static/spring-cloud.html。

大致意思就是bootstrap的配置会优先application。但官网只解释了用bootstrap替换application的问题。但从前面的异常我们发现,虽然uri依然采用的是默认值,但是application、profile和lable这三个值被我们自己定义的覆盖了。很显然依然解释不清楚问题2。

难道是Spring Cloud的官网解释的有问题?随着老王的思路一步一步排查

5.1 定位问题原因

找到打印异常信息的类,

c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://localhost:8888/. Will be trying the next url if available

Could not locate PropertySource: I/O error on GET request for "http://localhost:8888/application/dev/master": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect

发现时Spring Cloud Config自身类有一个uri默认值,也就是我们看到的ConfigClientProperties private String[] uri = { "http://localhost:8888" }。它使用了默认的值。

 那可以推断出项目中application.yml没有把uri值覆盖掉,而且除application.yml外,其他地方也没有单独配置uri的值。但是name,lable和profile是生效了。所以这么看并不是application.yml没有生效啊?

5.2 定位问题2分析过程

那究竟时怎么覆盖的呢?我们可以从发送http请求出问题的地方入手。

 

调用的是org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#getRemoteEnvironment方法。那需要排查前面是否有单独处理三个值的地方。我们会发现有一个方法:

 

ConfigClientProperties properties = this.defaultProperties.override(environment); 这个地方有一个覆盖值的方法,进到方法内部。会发现这三个值被重新覆盖了。

 

那这就奇怪了,为什么要覆盖?被谁覆盖了?再解析属性的地方打断点,我们继续调试跟踪。

发现是从application.yml中获取的值。

 那这就解释了为什么另外3个值会被覆盖的原因了。

5.3 定位问题1分析过程

解决了前面三个属性值覆盖问题,现在我们回头看uri的值为什么没被覆盖掉。

5.3.1 逆向排查

首先,我们需要看哪儿调用了这个类,发现这个类构造函数(仅有一个构造方法),

 

org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#ConfigServicePropertySourceLocator//构造函数。那此时我们就需要看谁调用了这个构造函数。

org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration#configServicePropertySource这个方法。

继续回退,看是谁调用了configServicePropertySource这个方法。

结果没有地方调用。而且构造函数也没有被调用。那就看类在那些位置被加载了。

此时,我们注意到有一个spring.factories文件。这个回到了我们熟悉的spring类扫描机制。我们看到,这个类被spring cloud的四个文件所引用。先看第一个文件,spring.factories。

 

我们知道,有时候希望一些第三方包被Spring管理,但是不想被Spring Boot扫描到。通常我们会采用注解进行实例化,另外一种是使用spring.factories机制。那这里的spring.factories就是通过这种方式管理Spring Cloud的包的。继续跟踪代码spring.factories文件。

这个就要看,什么时候加载并注入的spring cloud类。那我们就要从头开始跟踪代码。

5.3.2 正向跟踪

1)定位监听

我们从main函数开始跟踪,进入到我们所熟悉的run这个方法。

org.springframework.boot.SpringApplication#run(java.lang.String...)

 

很关键的两行代码,

// 关键点1:扫描所有包下实现了ApplicationListener接口的实现类,提前注入依赖参数。

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting(); //开启事件监听

try {

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //关键点2: 准备配置信息,这里是加载yml和properties的关键方法

}

 

此时,我们继续看BootstrapApplicationListener这个类是怎么样的处理机制?抓入口,看onApplicationEvent(ApplicationEnvironmentPreparedEvent event) 这个方法。这个是Spring Boot的上下文加载监听事件。

打断点继续跟踪。发现了一个很关键的信息,就是spring.config.name=bootstrap的信息。这个配置会影响后面如何加载配置文件的流程。

 

BootstrapApplicationListener加载完后,我们继续往后看。前面提到了两个关键点,第一个知道了其作用,那第二个是怎么触发的呢?继续看监听器列表,注意这个ConfigFileApplicationListener监听器,就是接下来加载配置文件(第二关键点的位置)。

2)定位配置

 

当执行到org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent时,

此处时根据不同事件,执行不同的处理逻辑。

 

此处的event为ApplicationEnvironmentPreparedEvent,会进入到org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent方法。

注意这句话,postProcessors.add(this); 表示将当前ConfigFileApplicationListener加到postProcess中。我们看org.springframework.boot.context.config.ConfigFileApplicationListener#postProcessEnvironment方法中调用了addPropertySources, 而addPropertySources中才是真正去调用加载配置文件的方法load()。

继续看load方法。

 

直接进入load的重载方法中,

 这个方法就是我们前面让大家记住的spring.config.name=bootstrap, 这个方法会影响加载资源文件的先后顺序。

此处逻辑为,如果配置了spring.config.name,就执行自己配置的。

 如果没有配置,则执行默认的,及application。

 而在我们的BootstrapApplicationListener中,已经写死了spring.config.name=bootstrap,所以此时走名称为bootstrap的配置文件,继续跟踪。

 

我们debug也发现,这个名称就是bootstrap。也就是会扫描这几个路径下的boorstrap.yml或bootstrap.properties文件。

 

 扫描路径,

 综上,我们知道了,SpringCloud会先加载名为bootstrap的配置文件。

3)定位注入

前面我们知道了spring cloud会加载名为bootstrap的问题,那再什么位置给ConfigClientProperties进行赋值处理的呢?这个就回到了Spring注入类是,需要将相关依赖都注入进来。

 所以在org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration注入时,会把所依赖的ConfigClientProperties类等也一起加载进来。

 

在调用栈中,我们看到,org.springframework.cloud.config.client.ConfigClientProperties是被当作参数初始化进来的。

 

 综上,我们知道了Spring Cloud会优先加载bootstrap.yml或bootstrap.properties文件。那Spring Cloud为什么要这么做呢?

从config中以及参考官网的一些说法,我们能看到,bootstrap.yml可以理解成优先级别高的一些参数配置,不想被其他配置覆盖。而application是偏应用层面的,可能会因不同环境而不同。从Spring Cloud的设计者角度考虑,认为像git这种配置,应该是固定不变的。

以上!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2.3-6循环链表

原理的单链表和循环单链表的区别&#xff1a; 初始化循环单链表时&#xff0c;使头节点next指针指向头节点。 判断循环单链表是否为空。 对比&#xff1a; 单链表&#xff1a;if(L->next NULL) 双链表&#xff1a;if(L->nextL) 判断循环单链表的结点p是否为表尾结点…

汇聚音视频新能量 探索行业新蓝海

视频行业卷成红海&#xff0c;如何突破瓶颈&#xff0c;去探索行业的新蓝海&#xff0c;本次LiveVideoStackCon 2022北京站邀请到快手高级副总裁、研发线负责人于冰&#xff0c;以《汇聚音视频新能量&#xff0c;探索行业新蓝海》为题&#xff0c;从视频行业趋势和痛点出发&…

类ChatGPT模型ChatGLM-b6本地部署实践

国外ChatGPT火爆持续&#xff0c;前一段时间百度发布“文心一言”还没有全面放开测试&#xff0c;这不阿里“通义千问”又悄然而至&#xff0c;国内大模型AI产品渐渐浮出水面。早在2022年8月份时候清华大学的对话语言模型ChatGLM-6B就发布并开源&#xff0c;本文简要介绍ChatGL…

flex弹性布局详细介绍

这里提供一个可以边学习边玩的flex学习网站&#xff1a;弹性盒青蛙 目录一、Flex布局是什么&#xff1f;二、属性1. justify-content 属性2. align-items属性3. flex-direction属性4. order属性5. align-self属性6. flex-wrap 属性7. flex-flow属性8. align-content属性三、综合…

测试新人必看,软件测试测试流程

不同类型的软件产品测试的方式和重点不一样&#xff0c;测试流程也会不一样。同样类型的软件产品&#xff0c;不同的公司所制定的测试流程也会不一样。虽然不同软件的详细测试步骤不同&#xff0c;但它们所遵循的最基本的测试流程是一样的。 1分析测试需求 2制定测试计划 3设…

Python opencv 实现图像平移及旋转

Python opencv 实现图像平移及旋转 仿射变换是一种仅在二维平面中发生的几何变形&#xff0c;变换之后的图像仍然可以保持直线的 “平直性” 和 “平行性”&#xff0c;也就是说原来的直线变换之后还是直线&#xff0c;平行线变换之后还是平行线。图像平移和图像旋转是常见的放…

IMX6ULL-IRQ中断之添加中断向量表

一. 中断向量表 中断向量表是存放中断向量的表。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量&#xff0c;因此中断向量表是一系列中断服务程序入口地址组成的表。当有中断事件发生时&#xff0c;处理器通过中断向量表进入相关的中断服务程序处理事件。 二.…

自动化测试(二):QTP验证点

1 、程序自带验证点 自带验证点&#xff1a;图形界面insert -> checkpoint Standard Checkpoint 标准验证&#xff1a;用于检查测试对象的属性 Text Checkpoint 文本验证&#xff1a;用于检查文本字符串是否在应用程序中的适当位置出现 Text Area Checkpoint文本区域验…

饮用水中的六价铬去除工艺详解

铬是人体必需的微量元素&#xff0c;天然水不含铬&#xff0c;海水中铬的平均浓度为0.05μg/L&#xff0c;饮用水中铬含量更低。 铬在水中主要以三价和六价形式存在&#xff0c;三价的铬是对人体有益的元素&#xff0c;而六价铬是有毒的。由于其毒性之高&#xff0c;已被国家列…

Smith预估器

Smith预估器主要针对存在大滞后的系统,作用延迟和反馈延迟环节的控制,Smith预估器的另一篇文章,请参看下面的博客文章: 博途1200/1500PLC Smith预估器(补偿器)算法实现(FB)_RXXW_Dor的博客-CSDN博客在写这篇文章之前写过一篇"大林控制算法",大家可以参看下面这…

免费的包噪音网站分享

免费的包噪音网站分享 现代生活中&#xff0c;噪音扰人&#xff0c;影响健康和情绪。白噪音可以为人们提供放松心情、提高睡眠质量和专注力的帮助。 现在有很多免费的白噪音网站可以任意使用和分享&#xff0c;包括海浪声、雨声、蝉鸣声等等&#xff0c;非常适合在办公室、家里…

windows 下安装 ffmpeg

介绍一下我的环境及开发软件版本 windows phpstudy php7.3 进入安装步骤 1.下载windows系统的FFMpeg 下载链接&#xff1a; http://ffmpeg.org/download.html ps: 这里有各种版本了 &#xff08;未使用这个版本&#xff09;git地址&#xff1a;https://github.com/BtbN/…

数据结构初阶 - 汇总

-0- 数据结构前言 什么是数据结构 什么是算法 数据结构和算法的重要性-1- 时间复杂度和空间复杂度 &#x1f449;数据结构 -1- 时间复杂度和空间复杂度 | C 算法效率 时间复杂度大O的渐进表示法eg 空间复杂度 常见复杂度对比OJ 消失的数组 轮转数组-2- 顺序表 与 链表 &am…

反射之构造方法和成员变量

什么是反射 反射允许对成员变量&#xff0c;成员方法和构造方法的信息进行编程访问。 Class类中获取构造方法的方法 Constructor getConstructors&#xff08;&#xff09; Constructor getDeclaredConstructors&#xff08;&#xff09; Constructor…

[STM32F103C8T6]看门狗

看门狗&#xff1a; 在由单片机构成的微型计算机系统中&#xff0c;由于单片机的工作常常会受到来自外界电磁场的干扰&#xff0c;造 成程序的跑飞&#xff0c;而陷入死循环&#xff0c;程序的正常运行被打断&#xff0c;由单片机控制的系统无法继续工作&#xff0c;会 造成整个…

LeetCode 1206. 设计跳表

LeetCode 1206. 设计跳表 难度&#xff1a;hard\color{red}{hard}hard 题目描述 不使用任何库函数&#xff0c;设计一个 跳表 。 跳表 是在 O(log(n))O(log(n))O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树&#xff0c;其功能与性能相当&am…

【区块链】【FISCO】WeIdentity

什么是 WeIdentity&#xff1f; 官方的说法&#xff1a;去中心化身份标识解决方案。其实说白了就是互联网上每个人都拥有自己数字身份&#xff0c;并且这个身份是唯一且不可篡改的。 WeIdentity要解决的问题就是用来解决数字身份验证的问题。传统互联网身份验证的方式通常用账…

将ip地址中的每一个字符串按照分隔符提取

1、算法思想 该题采用 c 中的 string 完成比较方便 对于字符串 string str1“hehehe:hahaha:xixixi:lalala” 定义 int pos 0&#xff0c;记录子串的初始位置 在循环语句中重复执行以下操作&#xff1a; &#xff08;1&#xff09;、定义 int ret str1.find(":",…

OpenAI-ChatGPT最新官方接口《聊天交互多轮对话》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(二)(附源码)

目录Chat completions Beta 聊天交互前言Introduction 导言Response format 提示格式Managing tokensCounting tokens for chat API calls 为聊天API调用标记计数Instructing chat models 指导聊天模型Chat vs Completions 聊天与完成FAQ 问与答其它资料下载Chat completions B…

27.Linux网络编程socket变成 tcp 高并发 线程池 udp

好&#xff0c;咱们开始上课了&#xff0c;从今天开始咱们连续讲 8 天的&#xff0c;网络编程这个还是在linux环境下去讲&#xff0c;咱们先看一下咱们这 8 天都讲什么东西&#xff0c;跟大家一块来梳理一下&#xff0c;你先有个大概的印象&#xff0c;这些你也不要记&#xff…