微服务配置管理
小编目前大一,刚开始着手学习微服务的相关知识,小编会把它们整理成知识点发布出来。我认为同为初学者,我把我对知识点的理解以这种代码加观点的方式分享出来不仅加深了我的理解,或许在某个时候对你也有所帮助,同时也欢迎大家在评论区分享你们的观点。
知不足而奋进,望远山而前行。
概述
在前面我们已经学习了远程调用,Nacos注册中心,OpenFeign以及网关这些微服务组件。但是我们现在又会面临新的问题,就是微服务、注册中心、网关之中存在许多重复配置,维护成本比较高,另外就是业务配置经常变动,每次修改都要重启服务非常麻烦,网关路由配置写死,如果变更要重启网关。那么现在我们就可以用到配置管理服务。
配置管理服务一方面读取配置,另一方面当配置发生改变时,它也可以去推送配置变更。这时可能很多人会疑惑那我们是不是需要去学习新的东西,其实这里是不用的,我们之前使用的注册中心Nacos同时也具备配置管理服务。
配置共享
那么接下来就来看一下如何运用Nacos实现配置共享,大概分为两步,第一步将一些要共享的配置添加到Nacos中。这一块直接利用Nacos提供的界面进行操作就好了。我们首先来到nacos左侧的导航栏,配置管理模块下的配置列表。
接着我们点击右侧的+号就可以去添加一个新的配置。一共需要必填三项Data ID、Group、配置内容。
Data ID 就相当于配置项的名字,分组这一块咱们先默认,因为这是第一个配置,也没什么号分组的。配置格式我们选择yaml格式,配置内容我们就把与jdbc有关的配置给CV过来,同时记得把一些可变参数都设置成待读取的变量以及可以的话给上默认值。
我已经带你完成了jdbc的配置,那日志的配置和swagger的配置你可不可以自己尝试做一下。现在这些共享配置已经添加到了nacos。接着第二步拉取共享配置,这一块就有点复杂了。
第二步拉取共享配置,我们要基于NacosConfig拉取共享配置代替微服务的本地配置。既然我们有了Nacos配置后,我们项目的读取配置流程就如下图,首先拉取Nacos配置,再初始化SpringCloud的上下文,接着再去加载application配置文件,完成SpringBoot上下文的初始化。
但是这一块就出问题了,我们是把Nacos的地址写在了application.yml中,因此这一块就矛盾了,一开始我们都不知道Nacos的地址怎么去拉取Nacos的配置呢。
在上面这种场景下SpringCloud就会有了一个新的配置文件叫做bootstrap.yml,叫做引导配置文件,项目一开始会从它开始启动,所以我们只需要将Nacos的地址写在bootstrap中就行了。
文件加载,拉取配置这些操作我们只需要引入对应依赖就好了。 接着我们再去新建bootstrap完成配置就好了。
首先第一步引入依赖这一块没什么好说的。
<!--nacos配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
接着第二步我们去编写bootstrap文件,在这个配置文件中,我们只需要定义服务名称和nacos的地址,以及定义好config中的配置文件名,file-extension定义的是nacos中配置的文件格式,shared-configs中定义的配置文件,这里设置的名字id就是我们在nacos中设置的id。
spring:
application:
name: cart-service # 微服务名称
profiles:
active: dev
cloud:
inetutils:
preferred-networks: 192.168.181.139
nacos:
server-addr: 192.168.181.139
config:
file-extension: yaml
shared-configs:
- data-id: shared-jdbc.yaml
- data-id: shared-log.yaml
- data-id: shared-swagger.yaml
这时我们再来看application.yml文件,这时我们所要书写的配置就寥寥无几了。一个是端口,一个是OpenFeign的连接池,接着就是定义我们配置的变量就好了。
server:
port: 8082
feign:
okhttp:
enabled: true # 开启Okhttp连接池支持
hm:
db:
database: hm-cart
swagger:
title: "黑马商城购物车服务接口文档"
package: com.hmall.cart.controller
到这里我们拉取配置就完成了,接着我们启动项目观察控制台输出,我们就可以发现那三个配置文件确实被加载到了。
接着测试一下功能是否还可以使用,当然也是没有问题的。
到此共享配置我们已经实现了。
配置热更新
刚刚我们已经实现了Nacos配置中心的第一个功能就是配置文件的配置共享,接着我们来看一下它的第二个功能配置热更新。
配置热更新就是当我们修改配置文件中的配置时,微服务无需重启即可使配置生效。但是要实现配置热更新我们要有一些前提条件,首先第一个要求就是nacos中要有一个与微服务名有关的配置文件。
其实这个文件名你其实是见过的,和下面bootstrap配置只能说一模一样。
spring:
application:
name: cart-service # 微服务名称
profiles:
active: dev
cloud:
inetutils:
preferred-networks: 192.168.181.139 # 允许nacos外网访问的地址
nacos:
server-addr: 192.168.181.139 # nacos 地址
config:
file-extension: yaml # 文件后缀名
shared-configs: # 共享配置
- data-id: shared-jdbc.yaml # 共享mybatis配置
- data-id: shared-log.yaml # 共享日志配置
- data-id: shared-swagger.yaml # 共享日志配置
第二个要求就是微服务当中要以特定方式读取需要热更新的配置属性。这个特定方式,一个是通过之前我们经常用的Config的方式,第二个就是通过Value注解来实现。这里我就采用第一种方式。
按照上面的步骤,首先在nacos中新增配置文件。
接着第二步书写配置类,我们设置了热更新属性maxItems。
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {
private Integer maxItems;
}
接着在使用到的地方进行替换。
private void checkCartsFull(Long userId) {
int count = lambdaQuery().eq(Cart::getUserId, userId).count();
if (count >= cartProperties.getMaxItems()) {
throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", cartProperties.getMaxItems()));
}
}
接着我们来测试一下购物车功能
确实显示没问题,接着我们来实现动态更新,不用重启服务,我们去更改配置中属性的值,也是可以成功的,这一块不好截图,自己尝试吧。
动态路由
之前我们都是把路由写死在了网关服务的配置文件中,但是倘若我们要更新路由,我们就得重启网关服务,但是网关服务对于一个项目来说是相当重要的,所以这一块我们要改变方案,我们要实现动态路由。
要实现动态路由首先要将路由配置保存到Nacos,当Nacos中的路由配置变更时,推送最新配置到网关,实现更新网关中的路由信息。我们主要要完成两件事情,第一件事情监听Nacos配置变更的消息,第二件事情当配置变更时,将最新的路由信息更新到网关路由表。
第一步监听Nacos配置变更信息其实Nacos已经给我们提供好了相应的API,我们直接使用就好了。官方文档传送入口如下:
Java SDK (nacos.io)
既然我们要做网关的配置管理,所以在网关服务下也要引入相关依赖。
<!--nacos配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
接着编写bootstrap文件,这一块和我们前面实现的时差不多的,只不过不需要数据库和swagger的配置了。
spring:
application:
name: gateway # 微服务名称
profiles:
active: dev
cloud:
inetutils:
preferred-networks: 192.168.181.139 # 允许nacos外网访问的地址
nacos:
server-addr: 192.168.181.139 # nacos 地址
config:
file-extension: yaml # 文件后缀名
shared-configs: # 共享配置
- data-id: shared-log.yaml # 共享日志配置
接下来我们就使用Nacos提供好的API来实现监听和更新路由信息。
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouterLoader {
private final NacosConfigManager nacosConfigManager;
private final RouteDefinitionWriter writer;
private final Set<String> routeIds = new HashSet<>();
private final String dataId = "gateway-routes.json";
private final String group = "DEFAULT_GROUP";
@PostConstruct
public void initRouteConfigListener() throws NacosException {
// 1. 项目启动时,先拉去一次配置,并且添加配置监听器
String configInfo = nacosConfigManager.getConfigService()
.getConfigAndSignListener(dataId, group, 5000, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
// 2. 监听到配置变更,需要去更新路由表
updateConfigInfo(configInfo);
}
});
// 3. 第一次读取到配置,也需要更新到路由表
updateConfigInfo(configInfo);
}
public void updateConfigInfo(String configInfo) {
log.debug("监听到路由配置信息:{}", configInfo);
// 1. 解析配置信息,转为RouteDefinition
List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
// 2. 删除旧的路由表
for (String routeId : routeIds) {
writer.delete(Mono.just(routeId)).subscribe();
}
// 3. 更新路由表
for (RouteDefinition routeDefinition : routeDefinitions) {
// 3.1 更新路由表
writer.save(Mono.just(routeDefinition)).subscribe();
// 3.2 记录路由id,便于下一次更新时删除
routeIds.add(routeDefinition.getId());
}
}
}
现在我们可以不用先去nacos配置路由信息,先启动服务,会发现根本访问不了,接着我们再去Nacos配置路由信息我们就可以实现动态路由了。
以上就是动态路由的相关知识,还是比较难的。
到此微服务配置管理这一块的基本知识就到此为止了。
带着决心起床,带着满意入睡。