到目前为止我们已经解决了微服务相关的几个问题:
- 微服务远程调用
- 微服务注册、发现
- 微服务请求路由、负载均衡
- 微服务登录用户信息传递
不过,现在依然还有几个问题需要解决:
- 网关路由在配置文件中写死了,如果变更必须重启微服务
- 某些业务配置在配置文件中写死了,每次修改都要重启服务
- 每个微服务都有很多重复的配置,维护成本高
这些问题都可以通过统一的配置管理器服务解决。而Nacos不仅仅具备注册中心功能,也具备配置管理的功能:
配置共享
一、添加配置到Nacos
添加一些共享配置到Nacos中,包括:jdbc、MybatisPlus、日志、Swagger、OpenFeign等配置
二、拉取共享配置
基于NacosConfig拉取共享配置代替微服务的本地配置
引入依赖
新建bootstrap.yaml
spring:
application:
name: cart-service
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.21.129:8848
config:
file-extension: yaml
shared-configs:
- data-id: shared-jdbc.yaml
- data-id: shared-log.yaml
- data-id: shared-swagger.yaml
之前的配置文件就可以改为下面这样
server:
port: 8082
feign:
okhttp:
enable: true
hm:
db:
database: hm-cart
swagger:
title: "商城购物车服务接口文档"
package: com.hmall.cart.controller
配置热更新
当修改文件中的配置时,微服务无需重启即可使配置生效
前提条件:
1.nacos中要有一个与微服务名有关的配置文件
2.微服务中要以特定方式读取需要热更新的配置属性
有两种方式
public class CartProperties {
private Integer maxItems;
}
动态路由
要实现动态路由首先要将路由配置保存到Nacos,当Nacos中的路由配置变更时,推送最新配置到网关,实时更新网关中的路由信息。
我们需要完成两件事情:
- 监听Nacos配置变更的消息
- 当配置变更时,将最新的路由信息更新到网关路由表
public class DynamicRouteLoader {
private final RouteDefinitionWriter writer;
private final NacosConfigManager nacosConfigManager;
// 路由配置文件的id和分组
private final String dataId = "gateway-routes.json";
private final String group = "DEFAULT_GROUP";
// 保存更新过的路由id
private final Set<String> routeIds = new HashSet<>();
@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) {
updateConfigInfo(configInfo);
}
});
// 2.首次启动时,更新一次配置
updateConfigInfo(configInfo);
}
private void updateConfigInfo(String configInfo) {
log.debug("监听到路由配置变更,{}", configInfo);
// 1.反序列化
List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
// 2.更新前先清空旧路由
// 2.1.清除旧路由
for (String routeId : routeIds) {
writer.delete(Mono.just(routeId)).subscribe();
}
routeIds.clear();
// 2.2.判断是否有新的路由要更新
if (CollUtils.isEmpty(routeDefinitions)) {
// 无新路由配置,直接结束
return;
}
// 3.更新路由
routeDefinitions.forEach(routeDefinition -> {
// 3.1.更新路由
writer.save(Mono.just(routeDefinition)).subscribe();
// 3.2.记录路由id,方便将来删除
routeIds.add(routeDefinition.getId());
});
}
}