作者为冰河忠实粉丝,可以加入【冰河技术】知识星球获取源码,也可以关注【冰河技术】公众号,加入社群学习,学习氛围好,干货满满。
1 现存问题
当我们的项目采用微服务架构后,原本单一的项目会被拆分为一个个小的微服务。原来项目中的配置文件就需要在每个微服务下都要存储一份,这些配置文件中的内容大部分都是相同的,只有个别的配置项不同。就拿数据库配置来说吧,如果每个微服务使用的技术栈都是相同的,则每个微服务中关于数据库的配置几乎都是相同的,有区别的地方可能就是:数据库连接,用户名和密码。
当项目采用微服务架构后,原本在单体项目中的配置文件就会散落在各个微服务中,如果不对这些散落的配置文件进行处理,就会造成各种各样的问题。总结起来,这些问题主要体现在如下几个方面。
- 配置文件散落在各个微服务项目中,随着整体项目的业务越来越复杂,后续会新增更多的微服务项目,微服务项目越来越多,则散落在微服务中的配置文件也会越来越多,后续会变得难以统一维护和管理。
- 配置文件散落在各个微服务项目中,还有一个非常棘手的问题,那就是修改配置文件非常麻烦。需要我们手动去修改每个微服务下的配置文件,稍有不慎,还会增加微服务项目出错的风险。
- 一般情况下,企业的项目发布环境包含开发环境、测试环境、预发布环境、生产环境。每个环境都需要对应不同的配置文件,如果不对这些配置文件进行统一管理,则每次发布到不同的环境时,都需要我们手动去修改每个微服务的配置文件,维护起来也是非常繁琐的。
- 在微服务中,手动修改了配置文件之后,修改后的具体的配置项无法在微服务项目中实时更新。每次修改配置文件后,都需要重新启动微服务项目。不管是从开发角度,还是从运维角度,都是非常不友好的。
2 配置中心
冰河:配置中心就是在微服务项目中,统一管理和维护项目配置信息的地方,它支持配置信息的集中存储,对外提供统一的接口获取配置,支持各个微服务主动调用配置中心的接口获取配置,也支持当配置信息发生变更时,由配置中心实时并且主动向各个微服务通知服务配置发生了变更,使其同步最新的配置信息。
在对配置中心的定义中,涵盖了三项重要内容。
- 配置中心将各个微服务中的配置进行统一集中管理和维护,并且对外提供统一的接口获取相关配置。
- 配置中心支持微服务主动调用配置中心的接口获取配置信息。
- 配置信息发生变更时,配置中心能够实时并且主动向各个微服务通知服务配置发生了变更,使其同步最新的配置信息。
这里,我们还是以商城微服务化后为例,当引入配置中心后,数据库配置信息,如下图所示。
可以看到,在微服务中引入配置中心后,配置信息不再存储到各个微服务项目中,也不用再手动修改每个微服务项目中的配置信息。而是在配置中心统一进行管理和维护。各个微服务会调用配置中心的接口获取配置信息。当配置中心的配置信息发生变更时,配置中心会主动并且实时通知各个微服务,使其获取配置中心的最新配置信息。
2.1 配置中心解决方案
配置中心 | 说明 |
---|---|
Consul | 谷歌基于Go语言开发出的一款支持服务动态发现的配置管理中心服务。在Consul中,内置了服务注册与发现功能,实现了分布式一致性协议,支持Key-Value存储,支持多数据中心。 |
Apollo | 协程开源的一款支持分布式的配置中心。支持修改后的配置动态实时生效,对项目的配置进行版本化管理,对配置的修改支持审计,支持项目的灰度发布。 |
Disconf | 百度开源的一款支持分布式的配置中心,主要是利用Zookeeper实现配置信息变更后,实时通知各个微服务,使变更后的配置信息生效。 |
SpringCloud Config | SpringCloud技术栈中自带的配置中心组件,支持使用Git仓库存储配置信息,不支持配置信息变更后实时生效。 |
Nacos | SpringCloud Alibaba技术栈中的一个在微服务环境下,支持分布式服务注册与发现,支持服务元数据及流量管理,支持分布式配置中心的组件。使用Nacos可以轻松实现服务的注册与发现,以及分布式配置中心功能。 |
2.2 Nacos配置中心概念
核心概念 | 中文说明 | 概念说明 |
---|---|---|
Namespace | 命名空间 | 主要用于不同环境下的配置隔离,不同的环境会被划分到不同的命名空间中。 |
Group | 配置分组 | 主要用于将不同的微服务划分到同一个分组中。通常情况下,会将组成整体项目的各个微服务的配置统一划分到同一个分组中。 |
Data ID | 配置集 ID | 通常情况下,在系统中,一个配置文件就是一个配置集,在这个配置文件中,能够包含系统各个方面的配置信息。配置集ID就是某个配置集的ID,也就是系统中某个配置文件的ID。 |
更多概念请参见:https://nacos.io/zh-cn/docs/concepts.html
3 项目整合Nacos配置中心
3.1 环境准备
分别在用户微服务shop-user,商品微服务shop-product,订单微服务shop-order和网关服务shop-gateway中的pom.xml文件中添加如下依赖。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
3.2 服务整合nacos配置中心
这里,我们将用户微服务,商品微服务,订单微服务和网关服务的配置信息都放在Nacos中。
注意:将微服务的配置放在Nacos中时,就暂时不用微服务中的application.yml配置文件了,而是在项目中新建一个bootstrap.yml文件。因为配置文件的优先级从高到低依次为:bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml。
3.2.1 用户微服务整合配置中心
- 在用户微服务shop-user的resources目录下新建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
- 启动Nacos,在浏览器中输入http://localhost:8848/nacos并登录Nacos,选择Nacos菜单中的配置管理-配置列表,如下所示。
点击配置列表界面中的 + ,如下所示。
其中各个配置项的含义如下所示。
Data ID就是配置集ID,通常情况下就是某个配置文件的ID,这个ID的命名遵循一定的规则,如下所示。
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
这里,对应到用户微服务的Data ID就是:server-user-dev.yaml。所以,对于用户微服务来说,我们在Data ID中输入server-user-dev.yaml。
- Group:配置分组,配置分组要与spring.cloud.nacos.config.group对应,用户微服务的配置分组就是user_group。
- 描述:对当前配置的简单描述。例如,用户微服务的开发环境配置。
- 配置格式:表示配置内容所使用的文件格式,这里我们选择YAML格式。
- 配置内容:配置文件的内容,这里我们直接将application.yml文件的内容复制进去即可。
配置好之后的内容如下所示。
点击发布按钮,发布成功后会跳转到配置管理-配置列表界面,如下所示。
- 注释掉用户微服务shop-user的resources目录下的application.yml文件中的内容(重要,不要忘记)
至此,就完成了修改用户微服务的配置操作。
3.2.2 商品微服务整合配置中心
此处和用户微服务配置过程相似,只是部分配置项不同。
- 在商品微服务shop-product的resources目录下新建bootstrap.yml文件,内容如下所示。
spring:
application:
name: server-product
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: product_group
profiles:
active: dev
- 在Nacos的配置列表中添加server-product-dev.yaml配置,设置Group设置为product_group,并将订单微服务中的application.yml内容复制过去,并将application.yml中配置注释掉。
3.2.3 订单微服务整合配置中心
此处和用户微服务配置过程相似,只是部分配置项不同。
- 在商品微服务shop-order的resources目录下新建bootstrap.yml文件,内容如下所示。
spring:
application:
name: server-order
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: order_group
profiles:
active: dev
- 在Nacos的配置列表中添加server-order-dev.yaml配置,设置Group设置为order_group,并将订单微服务中的application.yml内容复制过去,并将application.yml中配置注释掉。
3.2.4 网关服务整合配置中心
此处和用户微服务配置过程相似,只是部分配置项不同。
- 在商品微服务shop-gateway的resources目录下新建bootstrap.yml文件,内容如下所示。
spring:
application:
name: server-gateway
main:
allow-bean-definition-overriding: true
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: gateway_group
profiles:
active: dev
- 在Nacos的配置列表中添加server-gateway-dev.yaml配置,设置Group设置为gateway_group,并将订单微服务中的application.yml内容复制过去,并将application.yml中配置注释掉。
3.2.5 查看Nacos中的配置中心
将用户微服务、商品微服务、订单微服务、网关服务的配置添加到Nacos中后,Nacos中的配置管理-配置列表如下所示。
3.3 整合Nacos配置中心测试
- 分别启动Nacos、Sentinel、ZipKin、RocketMQ的NameServer和Broker。
- 分别启动用户微服务、商品微服务、订单微服务和网关服务,如果启动成功,基本就说明项目整合Nacos配置中心成功。
- 启动四个服务后,在浏览器中输入localhost:10001/server-order/order/submit_order?userId=1001&productId=1001&count=1,如下所示。
- 各服务也会打印相关日志,此处不做展示。
至此就成功整合了Nacos的配置中心。
4 实现动态刷新与配置共享
4.1 实现动态刷新
我们将微服务的配置放入了Nacos,但是此时如果在Nacos中修改了配置,程序是无法读取到修改后的配置的。所以,我们需要实现动态刷新的功能。
4.1.1 修改Nacos配置
在Nacos中修改server-user-dev.yaml的配置,在server-user-dev.yaml配置中新增如下内容。
author:
name: binghe
点击发布即可。
4.1.2 通过IOC容器获取Nacos配置
- 在用户微服务shop-user的cn.mawenda.shop.user.controller包下新建NacosController类,并在NacosController类中注入org.springframework.context.ConfigurableApplicationContext类,通过ConfigurableApplicationContext获取到系统环境变量,并从环境变量中获取到author.name属性的值,就是我们在Nacos中配置的作者姓名,如下所示。
/**
* @Author: Ma.wenda
* @Date: 2024-05-15 11:05
* @Version: 1.0
**/
@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;
}
}
- 启动用户微服务和网关服务,并在浏览器中输入http://localhost:10001/server-user/user/nacos/test,如下所示。
- 修改Nacos中server-user-dev.yaml的配置,将author.name的值修改为binghe001,如下所示。
- 发布后,不要重启用户微服务和网关服务,在浏览器上刷新http://localhost:10001/server-user/user/nacos/test链接,发现读取到的信息已经变成了binghe001,如下所示。
可以看到,通过IOC容器能够读取到Nacos中修改后的配置。
4.1.3 通过注解获取Nacos配置
- 在用户微服务shop-user的cn.mawenda.shop.user.controller.NacosController类上添加@RefreshScope注解,并将Nacos中的author.name的值注入到NacosController类中,并通过接口获取到注入的Nacos中的author.name值,如下所示。
/**
* @Author: Ma.wenda
* @Date: 2024-05-15 11:05
* @Version: 1.0
**/
@Slf4j
@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;
}
}
- 启动用户微服务和网关服务,并在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。
- 修改Nacos中server-user-dev.yaml的配置,将author.name的值修改为binghe002,如下所示。
- 发布后,不要重启用户微服务和网关服务,在浏览器上刷新http://localhost:10001/server-user/user/nacos/name链接,发现读取到的信息没有变成binghe002,如下所示。(此处和冰河大佬有差异,暂不清楚为什么)
解决办法,在NacosController类中增加@RefreshScope注解,实现了自动更新,后续再看下原理,作者此处指简单看了下该篇文章https://blog.csdn.net/qq_42425515/article/details/129746707,可供参考。
4.2 实现配置共享
一般情况下,开发项目的过程中会包含:开发环境、测试环境、预发布环境和生成环境,每个环境中的配置大部分是相同的,有少部分配置不同,如果我们能够将大部分相同的配置抽取出来,让同一个微服务在不同的环境中共享,则能够大大降低我们的维护成本。
另外,如果使用的是相同的技术栈的话,对于每个微服务来说,可能大部分的配置也是相同的,如果我们将这些相同的配置抽取出来让各个微服务共享,则能够进一步降低维护成本。
所以,实现配置共享可以从 单服务多环境配置和多服务配置 两个角度来思考。
4.2.1 单服务多环境实现共享
实现单个微服务在多个不同环境中的配置共享比较简单,只要在Nacos中配置一个以spring.application.name为Data ID的配置文件,并且将这个微服务在所有环境中公共的配置放进去即可。
注意:这里以用户微服务为例进行实现。
- 在Nacos中新建一个以server-user.yaml为Data ID的配置,并且将用户微服务在各个环境中共有的配置复制进去,如下所示。
点击发布。
- 将Nacos中用户微服务在开发环境下的配置server-user-dev.yaml修改成如下所示。
author:
name: binghe_dev
- 在Nacos中添加用户微服务在测试环境下的配置server-user-test.yaml,配置中的主要内容如下所示。
author:
name: binghe_test
- 修改后,Nacos配置中心的配置如下。
- 查看用户微服务中bootstrap.yml文件的配置,如下所示。
可以看到,此时bootstrap.yml文件中配置的是环境是dev,表示开发环境。
- 启动用户微服务和网关服务,在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。
- 将用户微服务的bootstrap.yml文件中的环境变量修改成test,如下所示。
spring:
profiles:
active: test
- 重启用户微服务,在浏览器中输入http://localhost:10001/server-user/user/nacos/name,如下所示。
至此,我们就实现了单个微服务在多个不同环境下的公共配置的共享操作。
4.2.1 多服务配置实现共享
不同的微服务之间实现公共的配置共享也比较简单,在Nacos中定义一个公共配置,然后在当前的配置中引入即可。具体的实现步骤如下所示。
- 在Nacos中新建一个以application.yaml为Data ID的配置,配置中的主要内容为用户微服务、商品微服务和订单微服务共有的配置,如下所示。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${MYSQL_HOST:shp-mysql}/wd-shop?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=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
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'
具体配置如下。
点击发布,并删除各服务的公共配置。
- 在Nacos中修改server-user.yaml的配置并发布,修改后的配置如下所示。
server:
port: 8060
servlet:
context-path: /user
spring:
application:
name: server-user
- 在Nacos中修改server-product-dev.yaml的配置并发布,修改后的配置如下所示。
server:
port: 8070
servlet:
context-path: /product
spring:
application:
name: server-product
- 在Nacos中修改server-order-dev.yaml的配置,修改后的配置如下所示。
server:
port: 8080
tomcat:
max-threads: 20
servlet:
context-path: /order
spring:
application:
name: server-order
feign:
sentinel:
enabled: true
- 修改用户微服务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
商品服务和订单服务的配置也增加如下配置,参考上述内容。
spring:
cloud:
nacos:
config:
shared-configs[0]:
data_id: server-all.yaml
group: all_group
refresh: true
- 分别启动用户微服务,商品微服务,订单微服务和服务网关,在浏览器中输入localhost:10001/server-order/order/submit_order?userId=1001&productId=1001&count=1,如下所示。
各服务均打印成功日志,此处不做展示,至此,我们的项目中成功整合了Nacos的配置中心,并实现了配置的动态刷新和共享。