专栏目录
0.docker快速入门
1.初识微服务
2.Gateway网关路由
3.Nacos配置管理
微服务拆分后,需要的重复配置过多,维护成本高
业务配置经常发生变动,每次修改都需要重启服务
网关路由配置写死后,每次变更都需要重启网关
因此需要通过统一的配置管理器服务解决。而Nacos不仅仅具备注册中心功能,也具备配置管理的功能。
微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。
网关的路由同样是配置,因此同样可以基于这个功能实现动态路由功能,无需重启网关即可修改路由配置。
把微服务共享的配置抽取到Nacos中统一管理,分为两步:
添加共享配置到Nacos,如JDBC、MybatisPlus、日志、Swagger、OpenFeign等配置
①在Nacos控制台中进行添加
图片内容来自:黑马程序员
②填写表单信息以及Yaml内容
图片内容来自:黑马程序员
一些相关的参数没有写死,而是使用${}覆盖,具体的值在application.yaml
中声明
从注册中心拉取共享配置后将会和本地的application.yaml
配置合并。
读取Nacos配置是SpringCloud上下文(
ApplicationContext
)初始化时处理的,发生在项目的引导阶段。
然后才会初始化SpringBoot上下文,去读取application.yaml
。
也就是说引导阶段,application.yaml
文件尚未读取,根本不知道nacos 地址
因此我们需要知道的是SpringCloud初始化上下文时首先读取bootstrap.yaml
文件,所有我们将nacos的地址配置到该文件即可
①引入相关依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
②新建bootstrap.yaml在resources目录下
spring:
application:
name: cart-service # 服务名称
profiles:
active: dev
cloud:
nacos:
server-addr: 192.168.150.101 # nacos地址
config:
file-extension: yaml # 文件后缀名
shared-configs: # 共享配置
- dataId: shared-jdbc.yaml # 共享mybatis配置
- dataId: shared-log.yaml # 共享日志配置
- dataId: shared-swagger.yaml # 共享日志配置
配置热更新:修改配置文件中的配置时,微服务无需重启即可使配置生效
前提条件:
@ConfigurationProperties(prefix="")
@RefreshScope+@Value("${}")
图片内容来自:黑马程序员
注意dataId的命名格式
①创建属性读取类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "demo.cart")
public class CartProperties {
private Integer maxAmount;
}
②业务中使用该类
private CartProperties cartProperties;
...
cartProperties.getMaxAmount();
...
自此直接在nacos注册中心修改demo.cart.maxAmount
的值即可生效
网关的路由配置在项目启动时一经加载就会缓存到内存中的路由表内(Map),不会改变,也不会监听路由变更。
因此不能使用上述热更新方法,而是要用Nacos提供的手动监听配置接口来实现
详情请见:Nacos监听配置变更-JavaSDK
①引入依赖
<dependency>
<groupId>com.alibaba.nacosgroupId>
<artifactId>nacos-clientartifactId>
dependency>
②使用Nacos动态监听配置接口
public void addListener(String dataId, String group, Listener listener);
参数名 | 参数类型 | 说明 |
---|---|---|
dataId | string | 配置 ID,保证全局唯一性。 只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“_”)。 |
group | string | 配置分组,一般是默认的DEFAULT_GROUP。 |
listener | Listener | 监听器,配置变更进入监听器的回调函数。 |
请求示例:
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{group}";
// 1.创建ConfigService,连接Nacos
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
// 2.读取配置
String content = configService.getConfig(dataId, group, 5000);
// 3.编写并添加配置监听器
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
// 配置变更的通知处理
System.out.println("recieve1:" + configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
使用监听删除接口
public void removeListener(String dataId, String group, Listener listener)
参数名 | 参数类型 | 描述 |
---|---|---|
dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“_”),不超过 256 字节。 |
group | string | 配置分组 |
listener | ConfigChangeListenerAdapter | 监听器,配置变更进入监听器的回调函数。 |
示例:
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{group}";
// 创建ConfigService,连接Nacos
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
// 删除监听
configService.removeListener(dataId, group, yourListener);
更新路由使用RouteDefinitionWriter
接口
/**
* @author Spencer Gibb
*/
public interface RouteDefinitionWriter {
/**
* 更新路由到路由表,如果路由id重复,则会覆盖旧的路由
*/
Mono<Void> save(Mono<RouteDefinition> route);
/**
* 根据路由id删除某个路由
*/
Mono<Void> delete(Mono<String> routeId);
}
- id:路由id
- predicates:路由匹配规则
- filters:路由过滤器
- uri:路由目的地
保存到nacos的配置格式要使用JSON保存:
{
"id": "item",
"predicates": [{
"name": "Path",
"args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
}],
"filters": [],
"uri": "lb://item-service"
}
等同于Yaml
spring:
cloud:
gateway:
routes:
- id: item
uri: lb://item-service
predicates:
- Path=/items/**,/search/**
①引入相关依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
②将gateway的resources目录创建bootstrap.yaml,添加nacos配置
spring:
application:
name: gateway
cloud:
nacos:
server-addr: ip地址
config:
file-extension: yaml
shared-configs:
- dataId: shared-log.yaml # 共享日志配置
③创建监听器配置类
import ...
@Slf4j
@Component
@RequiredArgsConstructor
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());
});
}
}
④在nacos注册中心后台添加路由
图片内容来自:黑马程序员