使用Nacos作为配置中心实现Gateway配置的自动更新
使用Nacos做为配置中心
-
在
bootstrap.yml
中配置好nacosnacos: gateway: route: config: data-id: e-commerce-gateway-router group: e-commerce
-
创建
NacosConfig
配置类读取bootstrap.yml
中的属性/** * @author qingtian * @description:配置类,读取Nacos中的配置项,用于配置监听器 * @Package com.imooc.ecommerce.config * @date 2021/12/7 0:41 */ @Configuration public class GatewayConfig { /** * 读取配置的超时时间 */ public static final long DEFAULT_TIMEOUT = 30000; /** * Nacos服务器地址 */ public static String NACOS_SERVER_ADDR; /** * 命名空间 */ public static String NACOS_NAMESPACE; /** * data_id */ public static String NACOS_ROUTER_DATA_ID; /** * 分组id */ public static String NACOS_ROUTER_GROUP; @Value("${spring.cloud.nacos.discovery.server-addr}") public void setNacosServerAddr(String nacosServerAddr) { NACOS_SERVER_ADDR = nacosServerAddr; } @Value("${spring.cloud.nacos.discovery.namespace}") public void setNacosNamespace(String nacosNamespace) { NACOS_NAMESPACE = nacosNamespace; } @Value("${nacos.gateway.route.config.data-id}") public void setNacosRouterDataId(String nacosRouterDataId) { NACOS_ROUTER_DATA_ID = nacosRouterDataId; } @Value("${nacos.gateway.route.config.group}") public void setNacosRouterGroup(String nacosRouterGroup) { NACOS_ROUTER_GROUP = nacosRouterGroup; } }
创建DynamicRouteServiceImpl
类
/**
* @author qingtian
* @description:事件推送 Aware :动态更新路由网关 Service
* @Package com.imooc.ecommerce.config
* @date 2021/12/7 15:55
*/
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
/**
* 写路由定义
*/
private final RouteDefinitionWriter routeDefinitionWriter;
/**
* 获取路由定义
*/
private final RouteDefinitionLocator routeDefinitionLocator;
/**
* 事件发布
*/
private ApplicationEventPublisher publisher;
public DynamicRouteServiceImpl(RouteDefinitionWriter routeDefinitionWriter
,RouteDefinitionLocator routeDefinitionLocator) {
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeDefinitionLocator = routeDefinitionLocator;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* 增加路由定义
* @param routeDefinition
* @return
*/
public String addRouteDefinition(RouteDefinition routeDefinition) {
log.info("gateway add route : [{}]",routeDefinition);
//保存路由配置并发布
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
//发布事件通知给gateway
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
/**
* 根据路由id删除路由
* @param id
* @return
*/
private String deleteById(String id) {
try {
log.info("gateway delete route id : [{}]",id);
routeDefinitionWriter.delete(Mono.just(id)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception ex) {
log.error("gateway delete route fail : [{}]",ex.getMessage(),ex);
return "delete dail";
}
}
/**
* 更新路由
* @param definition
* @return
*/
private String updateByRouteDefinition(RouteDefinition definition) {
try {
log.info("gateway update route : [{}]",definition);
//删除
routeDefinitionWriter.delete(Mono.just(definition.getId()));
}catch (Exception ex) {
log.error("update fail ,not find routeId : [{}]",definition.getId());
return "update fail"+definition.getId();
}
try {
log.info("gateway update route save : [{}]",definition);
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception exception) {
log.error("update fail : [{}]",definition.getId());
return "update fail";
}
}
/**
* 更新路由公有方法
* @param definitions
* @return
*/
public String updateList(List<RouteDefinition> definitions) {
log.info("gateway update route : [{}]",definitions);
//先拿到当前gateway中存储的路由定义
List<RouteDefinition> routeDefinitionsExist = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
if (!CollectionUtils.isEmpty(routeDefinitionsExist)) {
//清除之前所有配置的旧配置
routeDefinitionsExist.forEach(rd -> {
log.info("delete route definition : [{}]",rd);
deleteById(rd.getId());
});
}
//把更新的路由定义同步到gateway中
definitions.forEach(definition -> updateByRouteDefinition(definition));
return "success";
}
}
DynamicRouteServiceImpl
类实现ApplicationEventPublisherAware
接口用来之后发布config
配置变更的消息,这个类的作用是实现对Gateway config
的增加和修改
创建DynamicRouteServiceImplByNacos
类
/**
* @author qingtian
* @description:通过nacos下发动态路由配置,监听naocs中路由配置变更
* @Package com.imooc.ecommerce.config
* @date 2021/12/7 16:29
*/
@Component
@Slf4j
@DependsOn({"gatewayConfig"})
public class DynamicRouteServiceImplByNacos {
/**
* nacos配置服务
*/
private ConfigService configService;
private final DynamicRouteServiceImpl dynamicRouteService;
public DynamicRouteServiceImplByNacos(DynamicRouteServiceImpl dynamicRouteService) {
this.dynamicRouteService = dynamicRouteService;
}
/**
*初始化nacos config
* @return
*/
private ConfigService initConfigService() {
try {
Properties properties = new Properties();
properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR);
properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE);
return configService = NacosFactory.createConfigService(properties);
} catch (Exception ex) {
log.error("init gateway config error : [{}]",ex.getMessage(),ex);
return null;
}
}
/**
* 监听nacos 下发的动态路由配置
* @param dataId
* @param group
*/
private void dynamicRouteNacosListener(String dataId,String group) {
try {
//给Nacos Config客户端增加一个监听器
configService.addListener(dataId, group, new Listener() {
/**
* 自己提供线程池执行操作
* ,如果不提供则使用默认的线程池
* @return
*/
@Override
public Executor getExecutor() {
return null;
}
/**
* 监听器收到配置更新
* @param s
*/
@Override
public void receiveConfigInfo(String s) {
log.info("start to update config: [{}]",s);
List<RouteDefinition> definitionList = JSON.parseArray(s, RouteDefinition.class);
log.info("update route : [{}]",definitionList.toString());
//更新路由配置
dynamicRouteService.updateList(definitionList);
}
});
}catch (NacosException ex) {
log.error("dynamic update gateway config error : [{}],",ex.getMessage(),ex);
}
}
/**
* Bean在容器中构造完成之后会执行
*/
@PostConstruct
public void init() {
log.info("gateway route init ...");
try {
//初始化Nacos配置客户端
configService = initConfigService();
if (null == configService) {
log.error("init configService fail");
return;
}
//通过Nacos config并指定路由配置去获取路由配置
String configInfo = configService.getConfig(
GatewayConfig.NACOS_ROUTER_DATA_ID,
GatewayConfig.NACOS_ROUTER_GROUP,
GatewayConfig.DEFAULT_TIMEOUT
);
log.info("get current gateway config : [{}]",configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
if (CollectionUtil.isNotEmpty(definitionList)) {
for (RouteDefinition routeDefinition : definitionList) {
log.info("init gateway info : [{}]",routeDefinition.toString());
dynamicRouteService.addRouteDefinition(routeDefinition);
}
}
} catch (Exception ex) {
log.info("gateway config init has some error : [{}]",ex.getMessage(),ex);
}
//设置监听器
dynamicRouteNacosListener(GatewayConfig.NACOS_ROUTER_DATA_ID,GatewayConfig.NACOS_ROUTER_GROUP);
}
}
注意:使用了@DependsOn({"gatewayConfig"})
注解,表明了此类的初始化要依赖于
gatewayConfig
类,gatewayConfig
需要的此类之前完成初始化,否则读取不到NACOS_SERVER_ADDR
等属性。
再看到方法
init()
使用了注解@PostConstruct
表明在此bean的实例化结束的第一时间执行init()
方法,同时在init()
方法中创建了dynamicRouteNacosListener
用于监听Gateway config
在Nacos
中的更新
测试
-
启动
GatewayApplication
-
查看日志:
可以看到在log日志中成功打印出了
nacos
中的gateway
配置 -
验证
gateway config
配置监听自动更新功能-
在
nacos
中更改配置 -
查看日志是否更新
-
可以看出
gateway
成功监听到nacos
中的更新并且在本地进行了修改
-
评论区