SpringCloud (3) 配置中心
将一个大项目拆解成多个微服务时,就会导致:
①微服务重复配置过多
②业务配置经常变动,每次修改都要重启服务
③网关配置写死,如果变更需要重启网关Nacos不仅有"注册中心"的功能,针对以上问题,Nacos还提供了"配置管理"功能
1 添加配置到Nacos
添加一些共享配置到Nacos中,比如JDBC,MybatisPlus,日志(logback.xml)等
2 拉取配置
2.1 基于NacosConfig拉取配置
基于NacosConfig拉取共享配置代替本地微服务的配置,把原本基于SpringBoot加载配置文件的方式改为SpringCloud加载配置文件的方式
2.2 引入依赖
※注意alibaba-nacos-config依赖,如果版本太高可能就不适合本篇文章
<!--nacos配置管理,nacosConfig-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2023.0.1.2</version>
</dependency><!--读取bootstrap文件-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
<dependency>
2.3 创建bootstrap.yaml文件
共享配置管理:优先级低,在bootstrap.yaml文件中通过data-id指定生效
spring:application:name: engineering-----devprofiles:active: devcloud:nacos:server-addr: 123.123.123.123:8848config:file-extension: yaml #文件后缀名/配置格式shared-configs: #共享配置- data-id: share-datasource.yaml- data-id: share-administrators-phonenumber.yaml
专属配置管理:优先级高,根据Nacos中的Data Id自动与"微服务名-环境"进行匹配生效
例如在Nacos中创建配置的Data Id为engineering-----dev-dev.yaml,那么此配置就会对名为engineering-----dev的微服务的dev环境生效
3 配置热更新
修改nacos中的配置时,微服务无需重启即可实时获取最新配置
3.1 热更新条件
① Nacos中添加一个与"微服务名"命名的专属配置管理(共享配置管理不行)
② spring.application.name必须写在bootstrap.yaml中(不能写在application.yaml中)
3.2 微服务接收热更新数据
3.2.1 方式一
Prop类+@ConfigurationProperties(prefix = "aaa.bbb")注解
@Component
@ConfigurationProperties(prefix = "aaa.bbb")
public class TestProp {private String ccc;public String getCcc() {return ccc;}public void setCcc(String ccc) {this.ccc = ccc;}
}
在对应的xxxServiceImpl类中用@Autowired注入成员变量,该成员变量就能接收热更新数据
@Autowired
private TestProp testProp;
3.2.2 方式二
@RefreshScope注解+@Value("${aaa.bbb.ccc}")注解
在对应的xxxServiceImpl类用@RefreshScope注解,同时用@Value("${aaa.bbb.ccc}")注入成员变量,该成员变量就能接收热更新数据
@Service
@Transactional
@RefreshScope
public class GuardNetServiceImpl extends ServiceImpl<GuardNetDao, GuardNet> implements GuardNetService {@Value("${aaa.bbb.ccc}")private String ccc;
4 动态路由
网关启动时,会加载配置文件中的路由信息,然后保存在路由表(缓存)中,之后在处理请求的路由转发时,就无须再去读取配置文件,而是直接读取缓存即可
但这样就会存在一个问题,即使将gateway模块的配置文件的路由信息写在nacos中,而且能实现"配置热更新",也无法做到路由表的实时更新(因为路由表只在网关启动时读取配置文件,后续对配置文件的热更新不会再被重新读取)
4.1 动态路由实现原理
① 路由配置保存到nacos,通过"配置热更新"原理,能实时将最新的路由信息更新到网关gateway模块
② 在gateway模块中监听nacos的配置变更,将变更的路由信息更新到路由表
4.2 gateway模块引入依赖
<!--nacos配置管理,nacosConfig-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2023.0.1.2</version>
</dependency><!--读取bootstrap文件-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
4.3 新建"动态路由加载器类",做路由配置变更监听
package xyz.aboluo.gateway.routers;import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;import com.alibaba.fastjson2.JSON;
import reactor.core.publisher.Mono;@Component
public class DynamicRouteLoader {@Autowiredprivate NacosConfigManager nacosConfigManager;@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;private final String dataId = "gateway-route-dev.json";private final String group = "DEFAULT_GROUP";private Set<String> routeIds = new HashSet<>();@PostConstruct // 在类被初始化进SpringIOC容器时,就调用此方法public void initRouteConfigListener() throws Exception {// 拉取NacosConfig配置并添加监听器String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String s) {// 监听到配置变更,更新到路由表updateConfigInfo(s);}});// 首次启动时,将拉取的配置信息更新到配置表updateConfigInfo(configInfo);}public void updateConfigInfo(String configInfo) {// 删除旧的路由表for (String routeId : routeIds) {routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();}routeIds.clear();// 更新路由表for (RouteDefinition routeDefinition : JSON.parseArray(configInfo, RouteDefinition.class)) {// 更新路由表routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();// 记录路由id,便于下次更新时删除旧的路由表routeIds.add(routeDefinition.getId());}// 发布路由applicationEventPublisher.publishEvent(new RefreshRoutesEvent(routeDefinitionWriter));}
}
4.4 在nacos配置管理中新建路由规则的json格式配置
路由规则的json格式与yaml格式对照关系: