Gateway一般配置路由的方式有三种,代码、文件、注册中心,但个人感觉使用注册中心的方式去动态更新路由的方式更能契合项目,但是配置会比较麻烦,不如文件和代码去配置简单
Nacos加入配置:gateway-router
我这里使用的是Json格式
id:唯一id
predictates:断言,我使用的是Path断言
uri:真实请求的上下文路径
filters:过滤器配置,使用的Gateway提供的前置过滤器,用来去掉前缀
这里不重要,不多做解释,感兴趣可以去查一下官方文档
代码部分
ps:配置都比较简单,注释都在代码里(copy就能用😉)
1. bootstart.yml独立配置网关数据
# 这个地方独立配置, 是网关的数据, 代码 GatewayConfig.java 中读取被监听
nacos:
gateway:
route:
config:
data-id: gateway-router
group: GATEWAY_CLOUD_GROUP
2.新建GatewayConfig配置类,读取nacos相关的配置项、如果开启了权限,需要配置账号密码
@Configuration
@SuppressWarnings(value = "all")
public class GatewayConfig {
/* 读取配置的超时时间 */
public static final long DEFAULT_TIMEOUT = 30000;
/* nacos服务器地址 */
public static String NACOS_SERVER_ADDR;
/* 命名空间 */
public static String NACOS_NAMESPACE;
/* date-id */
public static String NACOS_ROUTE_DATE_ID;
/* 分组ID */
public static String NACOS_ROUTE_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 setNacosRouteDataId(String nacosRouteDataId){
NACOS_ROUTE_DATE_ID = nacosRouteDataId;
}
@Value("${nacos.gateway.route.config.group}")
public void setNacosRouteGroup(String nacosRouteGroup){
NACOS_ROUTE_GROUP = nacosRouteGroup;
}
}
3.创建DynamicRouteService类,用来做时间推送 Aware:动态更新路由网关 service,主要就是对路由的增删改查操作
注释很明确,都是增删改查的操作,只要 逻辑清楚点都能看懂
@Slf4j
@Service
@SuppressWarnings("all")
public class DynamicRouteService implements ApplicationEventPublisherAware/*spring提供的事件推送接口*/ {
/* 写gateway中的路由定义 */
private final RouteDefinitionWriter routeDefinitionWriter;
/* 获取路由定义 */
private final RouteDefinitionLocator routeDefinitionLocator;
/* 事件发布对象 */
private ApplicationEventPublisher publisher;
public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter, RouteDefinitionLocator routeDefinitionLocator) {
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeDefinitionLocator = routeDefinitionLocator;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
//完成时间推送句柄的初始化
this.publisher = applicationEventPublisher;
}
/**
* @Description 添加路由定义
* @Params [definition]
* @Return java.lang.String
* @Author JiaChaoYang
* @Date 2022/9/12 9:24
*/
public String addRouteDefinition(RouteDefinition/*读取出来的配置会到这里,网关*/ definition){
log.info("gateway add route: {}",definition);
/* 保存路由配置并发布 */
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
/* 发布事件通知给Gateway 同步新增路由定义 */
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
/**
* @Description 根据路由id去删除路由配置
* @Params [id]
* @Return java.lang.String
* @Author JiaChaoYang
* @Date 2022/9/12 9:29
*/
private String deleteById(String id){
try {
log.info("gateway delete route id : {}",id);
this.routeDefinitionWriter.delete(Mono.just(id));
//发布事件通知给gateway 更新路由定义
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "delete success";
}catch (Exception e) {
log.error("gateway delete route fail: {}",e.getMessage(),e);
return "delete fail";
}
}
/**
* @Description 更新路由
* @Params [routeDefinitionList]
* @Return java.lang.String
* @Author JiaChaoYang
* @Date 2022/9/12 9:36
*/
public String updateList(List<RouteDefinition> routeDefinitionList){
log.info("gateway update route: {}",routeDefinitionList);
//拿到当前gateway 中存储的路由定义
List<RouteDefinition> routeDefinitions = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
if (!CollectionUtils.isEmpty(routeDefinitions)){
//清除掉之前所有的旧的路由定义
routeDefinitions.forEach(rd ->{
log.info("delete route definition:",rd);
deleteById(rd.getId());
});
}
// 把更新的路由定义同步到gateway中
routeDefinitionList.forEach(definition -> updateByRouteDefinition(definition));
return "success";
}
/**
* @Description 更新路由,更新的实现策略比较简单:删除 + 新增 = 更新
* @Params [definition]
* @Return java.lang.String
* @Author JiaChaoYang
* @Date 2022/9/12 9:33
*/
private String updateByRouteDefinition(RouteDefinition definition){
try {
log.info("gateway update route : {}",definition);
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
}catch (Exception e) {
return "update fail , not find route routeId:"+ definition.getId();
}
try {
this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}catch (Exception e) {
return "update route fail";
}
}
}
4.最后一步我们去创建一个监听器DynamicRouteServiceImplByNacos,通过Nacos下发动态路由配置,监听Nacos中路由配置变更
@Slf4j
@Component
@DependsOn({"gatewayConfig"}) /*在另一个bean初始化后再初始化此bean*/
@SuppressWarnings("all")
public class DynamicRouteServiceImplByNacos {
/* Nacos配置服务客户端 */
private ConfigService configService;
@Resource
private DynamicRouteService dynamicRouteService;
/**
* @Description 在容器中构造完成后会立即执行
* @Params []
* @Return void
* @Author JiaChaoYang
* @Date 2022/9/13 11:54
*/
@PostConstruct //bean构造完成后,会立即执行
public void init(){
log.info("gateway route init ......");
try {
//初始化nacos配置客户端
configService = initConfigService();
if (configService == null){
//连不上nacos没必要执行了,直接return
log.error("init config service fail");
return;
}
//通过nacos config并指定路由配置去获取路由配置。获取到具体的配置信息,需要指定组,dataid
String configInfo = configService/*已经连接到nacos,并获取到了对应的命名空间*/.getConfig(
GatewayConfig.NACOS_ROUTE_DATE_ID,
GatewayConfig.NACOS_ROUTE_GROUP,
GatewayConfig.DEFAULT_TIMEOUT
);
log.info("get current gateway config : {}",configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo,RouteDefinition.class);
if (CollUtil.isNotEmpty(definitionList)){
for (RouteDefinition definitionDefinition : definitionList){
log.info("init gateway config {}",definitionDefinition.toString());
dynamicRouteService.addRouteDefinition(definitionDefinition);
}
}
}catch (Exception e) {
log.error("gateway route init has some error: {}",e.getMessage(),e);
}
//设置监听器
dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATE_ID,GatewayConfig.NACOS_ROUTE_GROUP);
}
/**
* @Description 初始化Nacos Config
* @Params []
* @Return com.alibaba.nacos.api.config.ConfigService
* @Author JiaChaoYang
* @Date 2022/9/12 9:53
*/
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 (NacosException e) {
log.error("init gateway nacos config error: {}",e.getMessage(),e);
return null;
}
}
/**
* @Description 实现对nacos的监听,nacos下发的动态路由配置信息
* @Params [dataId, group]
* @Return void
* @Author JiaChaoYang
* @Date 2022/9/13 11:17
*/
private void dynamicRouteByNacosListener(String dataId , String group){
try {
//给nacosconfig客户端增加一个监听器
configService.addListener(dataId, group, new Listener() {
//自己提供线程池执行操作
@Override
public Executor getExecutor() {
//为null是默认的线程池
return null;
}
/**
* @Description 监听器收到配置更新
* @Params [s] nacos中最新的配置定义
* @Return void
* @Author JiaChaoYang
* @Date 2022/9/13 11:21
*/
@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 e) {
log.error("dynamic update gateway config error: {}",e.getMessage(),e);
}
}
}