# SA实战 ·《SpringCloud Alibaba实战》第23章-服务配置:实现动态刷新与配置共享

作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)

大家好,我是冰河~~

一不小心《SpringCloud Alibaba实战 (opens new window)》专栏都更新到第23章了,再不上车就跟不上了,小伙伴们快跟上啊!

注意:本项目完整源码加入 冰河技术 (opens new window) 知识星球即可获取,文末有入场方式。

# 前文回顾

在《SpringCloud Alibaba实战 (opens new window)》专栏前面的文章中,我们实现了用户微服务、商品微服务和订单微服务之间的远程调用,并且实现了服务调用的负载均衡。也基于阿里开源的Sentinel实现了服务的限流与容错,并详细介绍了Sentinel的核心技术与配置规则。简单介绍了服务网关,并对SpringCloud Gateway的核心架构进行了简要说明,也在项目中整合了SpringCloud Gateway网关实现了通过网关访问后端微服务.

同时,也基于SpringCloud Gateway整合Sentinel实现了网关的限流功能,详细介绍了SpringCloud Gateway网关的核心技术。在链路追踪章节,我们开始简单介绍了分布式链路追踪技术与解决方案,随后在项目中整合Sleuth实现了链路追踪,并使用Sleuth整合ZipKin实现了分布式链路追踪的可视化 。

在消息服务章节,我们介绍了MQ的使用场景,引入MQ后的注意事项以及MQ的选型对比,在项目中整合了RocketMQ,并给大家介绍了RocketMQ的核心技术。

在服务配置章节,我们首先介绍了服务配置与Nacos作为配置中心的相关概念,并在项目中整合了Nacos配置中心。接下来,就基于Nacos实现动态刷新与配置共享。

# 本章总览

sa-2022-05-24-033

注意:本章中在测试每个案例时,都需要启动Nacos,Sentinel,ZipKin和RocketMQ。另外,本章对应的Nacos配置(冰河亲测的Nacos配置)放在了项目源码的doc/nacos/config/chapter23目录下,小伙伴们将Nacos的配置直接导入到Nacos的配置列表中,也可以按照文章自己手动实现。本项目完整源码加入 冰河技术 (opens new window) 知识星球即可获取,文末有入场方式。

# 实现动态刷新

细心的小伙伴可能已经发现了,之前我们将微服务的配置放入了Nacos,但是此时如果在Nacos中修改了配置,程序是无法读取到修改后的配置的。所以,我们需要实现动态刷新的功能。

注意:为了描述上的简便,这里,我们就以用户微服务举例说明,其他微服务的实现方式都是一样的。

# 修改Nacos配置

在Nacos中修改server-user-dev.yaml的配置,在server-user-dev.yaml配置中新增如下内容。

author:
  name: binghe
1
2

如下所示。

sa-2022-05-24-014

点击发布后会弹出如下提示。

sa-2022-05-24-015

直接点击确认发布即可。

# 通过IOC容器获取Nacos配置

(1)在用户微服务shop-user的io.binghe.shop.user.controller包下新建NacosController类,并在NacosController类中注入org.springframework.context.ConfigurableApplicationContext类,通过ConfigurableApplicationContext获取到系统环境变量,并从环境变量中获取到author.name属性的值,就是我们在Nacos中配置的作者姓名,如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 从Nacos中获取配置项
 */
@Slf4j
@RestController
public class NacosController {

    @Autowired
    private ConfigurableApplicationContext context;

    @GetMapping("/nacos/test")
    public String nacosTest(){
        String authorName = context.getEnvironment().getProperty("author.name");
        log.info("获取到的作者姓名为:{}", authorName);
        return authorName;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

(2)启动用户微服务和网关服务,并在浏览器中输入http://localhost:10001/server-user/user/nacos/test,如下所示。

sa-2022-05-24-016

可以看到,能够正确读取到Nacos中的配置信息。

(3)修改Nacos中server-user-dev.yaml的配置,将author.name的值修改为binghe001,如下所示。

sa-2022-05-24-017

点击发布。

(4)发布后,不要重启用户微服务和网关服务,在浏览器上刷新http://localhost:10001/server-user/user/nacos/test链接,发现读取到的信息已经变成了binghe001,如下所示。

sa-2022-05-24-018

可以看到,通过IOC容器能够读取到Nacos中修改后的配置。

# 通过注解获取Nacos配置

(1)在用户微服务shop-user的io.binghe.shop.user.controller.NacosController类上添加@RefreshScope注解,并将Nacos中的author.name的值注入到NacosController类中,并通过接口获取到注入的Nacos中的author.name值,如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 从Nacos中获取配置项
 */
@Slf4j
@RefreshScope
@RestController
public class NacosController {

    @Autowired
    private ConfigurableApplicationContext context;

    @Value("${author.name}")
    private String nacosAuthorName;

    @GetMapping("/nacos/test")
    public String nacosTest(){
        String authorName = context.getEnvironment().getProperty("author.name");
        log.info("获取到的作者姓名为:{}", authorName);
        return authorName;
    }

    @GetMapping("/nacos/name")
    public String nacosName(){
        log.info("从Nacos中获取到的作者的姓名为:{}", nacosAuthorName);
        return nacosAuthorName;
    }
}
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

(2)启动用户微服务和网关服务,并在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。

sa-2022-05-24-019

(3)修改Nacos中server-user-dev.yaml的配置,将author.name的值修改为binghe002,如下所示。

sa-2022-05-24-020

(4)发布后,不要重启用户微服务和网关服务,在浏览器上刷新http://localhost:10001/server-user/user/nacos/name链接,发现读取到的信息已经变成了binghe002,如下所示。

sa-2022-05-24-021

说明使用注解的方式也能够读取到Nacos中更新后的配置信息。

注意:虽然通过IOC容器和注解都能够读取到Nacos中更新后的配置信息,但是在实际项目中,推荐使用注解的方式获取Nacos中更新后的配置信息。

# 实现配置共享

一般情况下,开发项目的过程中会包含:开发环境、测试环境、预发布环境和生成环境,每个环境中的配置大部分是相同的,有少部分配置不同,如果我们能够将大部分相同的配置抽取出来,让同一个微服务在不同的环境中共享,则能够大大降低我们的维护成本。

另外,如果使用的是相同的技术栈的话,对于每个微服务来说,可能大部分的配置也是相同的,如果我们将这些相同的配置抽取出来让各个微服务共享,则能够进一步降低维护成本。

所以,实现配置共享可以从 单服务多环境配置和多服务配置 两个角度来思考。

# 单服务多环境实现共享

实现单个微服务在多个不同环境中的配置共享比较简单,只要在Nacos中配置一个以spring.application.name为Data ID的配置文件,并且将这个微服务在所有环境中公共的配置放进去即可。

注意:这里我们以用户微服务为例进行实现。

(1)在Nacos中新建一个以server-user.yaml为Data ID的配置,并且将用户微服务在各个环境中共有的配置复制进去,如下所示。

sa-2022-05-24-022

其中,我们假设用户微服务在多个环境下公共的配置如下所示。

server:
  port: 8060
  servlet:
    context-path: /user
spring:
  application:
    name: server-user
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initialSize: 10
    minIdle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 3600000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 3600000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    maxOpenPreparedStatements: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

  mybatis-plus:
    global-config:
      db-config:
        id-type: auto
        field-strategy: not-empty
        table-underline: true
        db-type: oracle
        logic-delete-value: 1
        logic-not-delete-value: 0
    mapper-locations: classpath:/mapper/*.xml
    configuration:
      jdbc-type-for-null: 'null'


rocketmq:
  name-server: 127.0.0.1:9876
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

点击发布。

(2)将Nacos中用户微服务在开发环境下的配置server-user-dev.yaml修改成如下所示。

author:
  name: binghe_dev
1
2

如下所示。

sa-2022-05-24-023

点击发布。

(3)在Nacos中添加用户微服务在测试环境下的配置server-user-test.yaml,配置中的主要内容如下所示。

author:
  name: binghe_test
1
2

具体如下所示。

sa-2022-05-24-024

点击发布。

(4)此时Nacos中的配置如下所示。

sa-2022-05-24-025

(5)查看用户微服务中bootstrap.yml文件的配置,如下所示。

spring:
  application:
    name: server-user
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: user_group
  profiles:
    active: dev
1
2
3
4
5
6
7
8
9
10
11

可以看到,此时bootstrap.yml文件中配置的是环境是dev,表示开发环境。

(6)启动用户微服务和网关服务,在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。

sa-2022-05-24-026

可以看到,正确获取到了Nacos中server-user-dev.yaml配置中的author.name的值。

(7)将用户微服务的bootstrap.yml文件中的环境变量修改成test,如下所示。

spring:
  profiles:
    active: test
1
2
3

(8)重启用户微服务,在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。

sa-2022-05-24-027

可以看到,正确获取到了Nacos中server-user-test.yaml配置中的author.name的值。

综上:我们正确实现了单个微服务在多个不同环境下的公共配置的共享操作。

# 多服务配置实现共享

不同的微服务之间实现公共的配置共享也比较简单,在Nacos中定义一个公共配置,然后在当前的配置中引入即可。具体的实现步骤如下所示。

(1)在Nacos中新建一个以server-all.yaml为Data ID的配置,配置中的主要内容为用户微服务、商品微服务和订单微服务共有的配置,如下所示。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initialSize: 10
    minIdle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 3600000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 3600000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    maxOpenPreparedStatements: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

  mybatis-plus:
    global-config:
      db-config:
        id-type: auto
        field-strategy: not-empty
        table-underline: true
        db-type: oracle
        logic-delete-value: 1
        logic-not-delete-value: 0
    mapper-locations: classpath:/mapper/*.xml
    configuration:
      jdbc-type-for-null: 'null'
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

具体配置如下所示。

sa-2022-05-24-028

点击发布,接下来就是在Nacos中修改各个微服务的配置,将各个微服务的配置中公共的配置删除。

(2)在Nacos中修改server-user.yaml的配置,修改后的配置如下所示。

server:
  port: 8060
  servlet:
    context-path: /user
spring:
  application:
    name: server-user
rocketmq:
  name-server: 127.0.0.1:9876
1
2
3
4
5
6
7
8
9

具体如下所示。

sa-2022-05-24-029

点击发布。

(3)在Nacos中修改server-product-dev.yaml的配置,修改后的配置如下所示。

server:
  port: 8070
  servlet:
    context-path: /product
spring:
  application:
    name: server-product
1
2
3
4
5
6
7

具体如下所示。

sa-2022-05-24-030

点击发布。

(4)在Nacos中修改server-order-dev.yaml的配置,修改后的配置如下所示。

server:
  port: 8080
  tomcat:
    max-threads: 20
  servlet:
    context-path: /order
spring:
  application:
    name: server-order

  cloud:
    sentinel:
      transport:
        port: 9999
        dashboard: 127.0.0.1:8888
      web-context-unify: false

feign:
  sentinel:
    enabled: true

rocketmq:
  name-server: 127.0.0.1:9876
  producer:
    group: order-group
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

具体如下所示。

sa-2022-05-24-031

点击发布。

(5)修改用户微服务shop-user中的bootstrap.yml文件,修改后的配置如下所示。

spring:
  application:
    name: server-user
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: user_group
        shared-configs[0]:
          data_id: server-all.yaml
          group: all_group
          refresh: true

  profiles:
    active: dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

(6)修改商品微服务shop-product中的bootstrap.yml文件,修改后的配置如下所示。

spring:
  application:
    name: server-product
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: product_group
        shared-configs[0]:
          data_id: server-all.yaml
          group: all_group
          refresh: true
  profiles:
    active: dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

(7)修改订单微服务shop-order中的bootstrap.yml文件,修改后的配置如下所示。

spring:
  application:
    name: server-order
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: order_group
        shared-configs[0]:
          data_id: server-all.yaml
          group: all_group
          refresh: true
  profiles:
    active: dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

(8)分别启动用户微服务,商品微服务,订单微服务和服务网关,在浏览器中输入localhost:10001/server-order/order/submit_order?userId=1001&productId=1001&count=1,如下所示。

sa-2022-05-24-032

可以看到,浏览器中正确输出了结果信息。

查看各个微服务中也会打印出相关的日志,如下所示。

  • 用户微服务会打印出如下信息
获取到的用户信息为:{"address":"北京","id":1001,"password":"c26be8aaf53b15054896983b43eb6a65","phone":"13212345678","username":"binghe"}
用户微服务收到了订单信息:{"address":"北京","id":15235872727371776,"phone":"13212345678","totalPrice":2399.00,"userId":1001,"username":"binghe"}
1
2
  • 商品微服务会打印出如下信息
获取到的商品信息为:{"id":1001,"proName":"华为","proPrice":2399.00,"proStock":9999}
更新商品库存传递的参数为: 商品id:1001, 购买数量:1 
1
2
  • 订单微服务会打印出如下信息
提交订单时传递的参数:{"count":1,"empty":false,"productId":1001,"userId":1001}
库存扣减成功
1
2
  • 网关服务会打印出如下信息
执行前置过滤器逻辑
执行后置过滤器逻辑
访问接口主机: localhost
访问接口端口: 10001
访问接口URL: /server-order/order/submit_order
访问接口URL参数: userId=1001&productId=1001&count=1
访问接口时长: 863ms
1
2
3
4
5
6
7

说明我们的项目中成功整合了Nacos的配置中心,并实现了配置的动态刷新和共享。

好了,今天我们就到儿吧,限于篇幅,文中并未给出完整的案例源代码,想要完整源代码的小伙伴可加入【冰河技术】知识星球获取源码。也可以加我微信:hacker_binghe,一起交流技术。

另外,一不小心就写了23章了,小伙伴们你们再不上车就真的跟不上了!!!

# 星球服务

加入星球,你将获得:

1.项目学习:微服务入门必备的SpringCloud Alibaba实战项目、手写RPC项目—所有大厂都需要的项目【含上百个经典面试题】、深度解析Spring6核心技术—只要学习Java就必须深度掌握的框架【含数十个经典思考题】、Seckill秒杀系统项目—进大厂必备高并发、高性能和高可用技能。

2.框架源码:手写RPC项目—所有大厂都需要的项目【含上百个经典面试题】、深度解析Spring6核心技术—只要学习Java就必须深度掌握的框架【含数十个经典思考题】。

3.硬核技术:深入理解高并发系列(全册)、深入理解JVM系列(全册)、深入浅出Java设计模式(全册)、MySQL核心知识(全册)。

4.技术小册:深入理解高并发编程(第1版)、深入理解高并发编程(第2版)、从零开始手写RPC框架、SpringCloud Alibaba实战、冰河的渗透实战笔记、MySQL核心知识手册、Spring IOC核心技术、Nginx核心技术、面经手册等。

5.技术与就业指导:提供相关就业辅导和未来发展指引,冰河从初级程序员不断沉淀,成长,突破,一路成长为互联网资深技术专家,相信我的经历和经验对你有所帮助。

冰河的知识星球是一个简单、干净、纯粹交流技术的星球,不吹水,目前加入享5折优惠,价值远超门票。加入星球的用户,记得添加冰河微信:hacker_binghe,冰河拉你进星球专属VIP交流群。

# 星球重磅福利

跟冰河一起从根本上提升自己的技术能力,架构思维和设计思路,以及突破自身职场瓶颈,冰河特推出重大优惠活动,扫码领券进行星球,直接立减149元,相当于5折, 这已经是星球最大优惠力度!


领券加入星球,跟冰河一起学习《SpringCloud Alibaba实战》、《手撸RPC专栏》和《Spring6核心技术》,更有已经上新的《大规模分布式Seckill秒杀系统》,从零开始介绍原理、设计架构、手撸代码。后续更有硬核中间件项目和业务项目,而这些都是你升职加薪必备的基础技能。

100多元就能学这么多硬核技术、中间件项目和大厂秒杀系统,如果是我,我会买他个终身会员!

# 其他方式加入星球

特别提醒: 苹果用户进圈或续费,请加微信 hacker_binghe 扫二维码,或者去公众号 冰河技术 回复 星球 扫二维码加入星球。

# 星球规划

后续冰河还会在星球更新大规模中间件项目和深度剖析核心技术的专栏,目前已经规划的专栏如下所示。

# 中间件项目

  • 《大规模分布式定时调度中间件项目实战(非Demo)》:全程手撸代码。
  • 《大规模分布式IM(即时通讯)项目实战(非Demo)》:全程手撸代码。
  • 《大规模分布式网关项目实战(非Demo)》:全程手撸代码。
  • 《手写Redis》:全程手撸代码。
  • 《手写JVM》全程手撸代码。

# 超硬核项目

  • 《从零落地秒杀系统项目》:全程手撸代码,在阿里云实现压测(已上新)。
  • 《大规模电商系统商品详情页项目》:全程手撸代码,在阿里云实现压测。
  • 其他待规划的实战项目,小伙伴们也可以提一些自己想学的,想一起手撸的实战项目。。。

既然星球规划了这么多内容,那么肯定就会有小伙伴们提出疑问:这么多内容,能更新完吗?我的回答就是:一个个攻破呗,咱这星球干就干真实中间件项目,剖析硬核技术和项目,不做Demo。初衷就是能够让小伙伴们学到真正的核心技术,不再只是简单的做CRUD开发。所以,每个专栏都会是硬核内容,像《SpringCloud Alibaba实战》、《手撸RPC专栏》和《Spring6核心技术》就是很好的示例。后续的专栏只会比这些更加硬核,杜绝Demo开发。

小伙伴们跟着冰河认真学习,多动手,多思考,多分析,多总结,有问题及时在星球提问,相信在技术层面,都会有所提高。将学到的知识和技术及时运用到实际的工作当中,学以致用。星球中不少小伙伴都成为了公司的核心技术骨干,实现了升职加薪的目标。

# 联系冰河

# 加群交流

本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “冰河” 微信(hacker_binghe),备注:星球编号

冰河微信

# 公众号

分享各种编程语言、开发技术、分布式与微服务架构、分布式数据库、分布式事务、云原生、大数据与云计算技术和渗透技术。另外,还会分享各种面试题和面试技巧。内容在 冰河技术 微信公众号首发,强烈建议大家关注。

公众号:冰河技术

# 视频号

定期分享各种编程语言、开发技术、分布式与微服务架构、分布式数据库、分布式事务、云原生、大数据与云计算技术和渗透技术。另外,还会分享各种面试题和面试技巧。

视频号:冰河技术

# 星球

加入星球 冰河技术 (opens new window),可以获得本站点所有学习内容的指导与帮助。如果你遇到不能独立解决的问题,也可以添加冰河的微信:hacker_binghe, 我们一起沟通交流。另外,在星球中不只能学到实用的硬核技术,还能学习实战项目

关注 冰河技术 (opens new window)公众号,回复 星球 可以获取入场优惠券。

知识星球:冰河技术