SpringCloud学习笔记(5)

Config

SpringCloud Config 分布式配置中心

概述

微服务意味着要将单应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

springCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理。比如数据库的信息,我们可以写到一个统一的地方。

  • config+bus
  • alibaba nacos
  • 携程 阿波罗

SpringCloud Config是什么:Spring Cloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置

怎么玩:
SpringCloud Config分为服务端和客户端两部分。

  • 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
  • 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容

能干嘛:

  • 集中管理配置文件
  • 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
  • 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
  • 将配置信息以REST接囗的形式暴露

config服务端配置

这个服务端指的是消费端与github之间的桥接

首先在github上新建一个仓库 springcloud-config

git@github.com:名字/项目.git

然后使用git命令克隆到本地,命令:git clone https://github.com/LZXYF/springcloud-config

注意上面的操作不是必须的,只要github上有就可以,克隆到本地只是修改文件。

常用命令:

  • git add
  • git commit -m “标记”
  • git push origin master

在git根目录下创建

  • 开发环境:config-dev.yml
  • 生产环境:config-pro.yml
  • 测试环境:config-test.tml

注意格式

新建 config-center3344 模块:

pom文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?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">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.wzg.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>config-center3344</artifactId>

<dependencies>
<!-- config Server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

<!--eureka-client config Server也要注册进服务中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.wzg.springcloud</groupId>
<artifactId>api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>


<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>

yml 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server:
port: 3344

spring:
application:
name: config-center #注册进Eureka服务器的微服务名
cloud:
config:
server:
git: # 此处使用的是老师是配置中心
uri: https://github.com/yztldxdza/springcloud-config.git #GitHub上面的git仓库名字
####搜索目录
search-paths:
- springcloud-config
####读取分支
label: master

#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka

主启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wzg.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

/**
* @author whlie(true){learn}
*/
@SpringBootApplication
@EnableConfigServer //关键注解
public class ConfigCenterMain3344 { // 注意先去把Eureka启动起来
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class,args);
}
}

添加模拟映射:【C:\Windows\System32\drivers\etc\hosts】文件中添加: 127.0.0.1 config-3344.com

启动微服务3344,访问 http://config-3344.com:3344/master/config-dev.yml 文件(注意,要提前在git上弄一个这文件)

文件命名和访问的规则:

1597646186970

不加分支名默认是master:

1597646308915

最后一个出的是json串

  • label:分支branch
  • name:服务名
  • profiles:环境dev/test/prod

config客户端配置

这里的客户端指的是,使用 Config Server 统一配置文件的项目。既有之前说的消费者,又有提供者

新建 config-client-3355 模块:

pom文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?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">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.wzg.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>config-client-3355</artifactId>

<dependencies>
<!-- config Client 和 服务端的依赖不一样 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

<!--eureka-client config Server也要注册进服务中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.wzg.springcloud</groupId>
<artifactId>api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>


<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>

要将Client模块下的application.yml文件改为bootstrap.yml,删掉application.yml这是很关键的

因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml

appllication.yml是用户级的资源配置项

bootstrap.ym1是系统级的,优先级更加高

SpringCloud会创建一个”Bootstrap Context”作为Spring应用的ApplicationContext的父上下文。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment

Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。BootstrapContext和ApplicationContext、有着不同的约定,所以新增了一个bootstrap.yml文件,保证BootstrapContext和ApplicationContext配置的分离。

bootstrap.yml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server:
port: 3355

spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称,文件也可以是client-config-dev.yml这种格式的,这里就写 client-config
profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
# 综合上面四个 即读取配置文件地址为: http://config-3344.com:3344/master/config-dev.yml

#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka


# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"

主启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wzg.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
* @author whlie(true){learn}
*/
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355{
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class, args);
}
}

controller层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.wzg.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author whlie(true){learn}
*/
@RestController
@RefreshScope
public class ConfigClientController {

//相当于配置了config后,就把config服务端里的变量引入进来了
@Value("${config.info}")
private String configInfo;

@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}

启动测试完成!如果报错,注意github上的 yml 格式有没有写错!

启动Config配置中心3344微服务并自测,启动3355作为client访问 localhost:3355/configInfo

修改config-dev.yml配置文件并提交到github中,比如加个变量age或者版本号version。更改消费者端的配置看看其他环境能不能用

动态刷新

问题:

  • Linux运维修改GitHub上的配置文件内容做调整:比如修改config-dev.yml提交
  • 刷新3344,发现ConfigServer服务端配置中心立刻响应,得到最新值了
  • 刷新3355,发现ConfigClient客户端没有任何响应,拿到的还是旧值
  • 客户端3355没有变化除非自己重启或者重新加载,才能拿到最新值
  • 难到每次运维修改配置文件,客户端都需要重启??噩梦

就是github上面配置更新了,config Server 项目上是动态更新的,但是,client端的项目中的配置,目前还是之前的,它不能动态更新,必须重启才行。

动态刷新问题解决:

  1. client端一定要有actuator依赖:

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. client 端增加 yml 配置如下,即在 bootstrap.yml 文件中:

1
2
3
4
5
6
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"

3.在controller 上添加注解@RefreshScope

到此为止,配置已经完成,但是测试客户端 localhost:3355/configInfo 仍然不能动态刷新,还是旧值(也就是说环境变量里的还是旧值),需要下一步。

4.向 client 端发送一个 POST 请求

如 curl -X POST “http://localhost:3355/actuator/refresh”

两个必须:1.必须是 POST 请求,2.请求地址:http://localhost:3355/actuator/refresh

成功获得到最新值

但是又有一个问题,就是要向每个微服务客户端发送一次POST请求,当微服务数量庞大,又是一个新的问题。

能否广播,一次通知,处处生效?(还要求不要全广播,差异化管理,定点清除,20台只有18台更新)

就有下面的消息总线!

Bus

spring cloud Bus配置spring cloud Config使用可以实现配置的动态刷新

spring cloud bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了java的事件处理机制和消息中间件的功能。

spring cloud bus目前支持RabbitMQ和Kafka(因为是主题订阅)

什么是总线

在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便地广播一些需要让貝他连接在该主题上的实例都知道的消息。

基本原理:

ConfigClient实例都监听MQ中同一个topic主题(默认是springCloud Bus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一topic的服务就能得到通知,然后去更新自身的配置

Bus能管理和传输分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当做微服务间的通信通道

RabbitMQ

在windows 上安装RabbitMQ

  1. 安装RabbitMQ的依赖环境 Erlang 下载地址: http://erlang.org/download/otp_win64_21.3.exe
  2. 安装RabbitMQ 下载地址: http://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
  3. 进入 rabbitMQ安装目录的sbin目录下,打开cmd窗口,执行 【rabbitmq-plugins enable rabbitmq_management
  4. 访问【http://localhost:15672/】,输入密码和账号:默认为guest

广播式刷新配置

  • 必须先具有良好的RabbitMQ环境

  • 演示广播效果,增加复杂度,再以3355为模板再制作一个3366

  • 设计思想

    • 1)利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置

    • 2)利用消息总线触发一个服务端ConfigServer的/bus/refres端点,从而刷新所有客户端的配置

    • 图二的架构显然更加适合,图一不适合的原因如下

      • 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责

      • 破坏了微服务各节点的对等性。

      • 有一定的局限性“例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的

  • 给cloud-config-center-3344配置中心服务端添加消息总线支持

  • 给cloud-config-client-3355客户端添加消息总线支持

  • 给cloud-config-client-3366客户端添加消息总线支持(以3355为模板)

  • 测试

  • 一次修改,广播通知,出处生效

  • 但还是得发一个POST请求,只不过只给config发而已

首先给 config Server 和 config client 都添加如下依赖:

1
2
3
4
5
<!-- 添加rabbitMQ的消息总线支持包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

config Server 的yml文件增加如下rabbitmq配置:

1
2
3
4
5
6
7
8
9
10
11
12
# rabbitMq的相关配置
rabbitmq:
host: localhost
port: 5672 # 这里没错,虽然rabbitMQ网页是 15672
username: guest
password: guest
# rabbitmq 的相关配置2 暴露bus刷新配置的端点
management:
endpoints:
web:
exposure:
include: 'bus-refresh'

config Client 的yml文件修改成如下配置:(注意对齐方式,和config Server不一样)

1
2
3
4
5
6
7
8
spring:
cloud:
# config 客户端配置
config:
label: master # 分支名称
name: client-config # 配置文件名称
profile: test # 使用配置环境
uri: http://config-3344.com:3344 # config Server 地址

3355客户端的bootstrap.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
server:
port: 3355 # client

spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址k

#rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest

#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka

# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"

3344注册中心的application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
server:
port: 3344

spring:
application:
name: cloud-config-center #注册进Eureka服务器的微服务名
cloud:
config:
server:
git:
uri: git@github.com:zzyybs/springcloud-config.git #GitHub上面的git仓库名字
####搜索目录
search-paths:
- springcloud-config
####读取分支
label: master
#rabbitmq相关配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest

#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka

##rabbitmq相关配置,暴露bus刷新配置的端点
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'

可在github上修改yml文件进行测试,修改完文件,向 config server 发送 请求:

给3344发就能全局同步了【curl -X POST “http://localhost:3344/actuator/bus-refresh”】

注意,之前是向config client 一个个发送请求,但是这次是向 config Server 发送请求,而所有的config client 的配置也都全部更新。

定点通知

新的需求:指定具体某一个实例(的参数)生效而不是全部,一些是最新值,一些是旧值

  • 公式:http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
  • 例子:curl -X POST “http//localhost:3344/actuator/bus-refresh/config-client:3355
  • 即微服务名称+端囗号
  • /bus/refresh请求不再发送到具体的服务实例上,而是发给configserver并通过destination参数类指定需要更新配置的服务或实例
  • 我们这里以刷新运行在3355端口上的config-client为例
    • 只通知3355
    • 不通知3366


SpringCloud学习笔记(5)
https://yztldxdz.top/2022/09/20/SpringCloud学习笔记(5)/
发布于
2022年9月20日
许可协议