主页 > 电脑硬件  > 

SpringCloud系列教程(十二):网关配置动态路由

SpringCloud系列教程(十二):网关配置动态路由

除了token以外,还有一个很实用的功能就是把网关的路由配置放到nacos上,并且修改路由配置的时候,网关服务可以动态的更新,这样我们在调整网络配置的时候,就不用重启服务了。所以我们需要用到两个重要的类:NacosConfigManager和RouteDefinitionWriter,NacosConfigManager用来监听nacos上配置的变化,RouteDefinitionWriter会执行路由配置。

1、将原来写在application.yml中的路由配置信息迁移到nacos上,dataId定义为gateway-routes,这里要注意,原来在项目中yml文件解析的时候,会被SpringCloud层层解析后生成RouteDefinition,在这期间SpringCloud会去解析比如Path=/api/*这种形式,但是现在我们要自己解析,就要改成符合RouteDefinition的完全形式,这是一个小难点。

spring: cloud: gateway: routes: - id: nacos-client-demo uri: lb://nacos-client-demo predicates: #断言,匹配访问的路径 - name: Path args: pattern: /nacos-client-demo/api/** filters: # 过滤器 - name: AddRequestHeader args: _genkey_0: headername _genkey_1: I am a header! - name: My args: _genkey_0: zhangsan _genkey_1: lisi _genkey_2: wangwu - id: open-feign-demo uri: lb://open-feign-demo predicates: - name: Path args: pattern: /open-feign-demo/api/**

2、在application.yml中添加nacos的配置信息就可以了,并且我特意添加了两个参数,用来指明动态路由配置的dataId,这样以后改成其他的也方便。

spring: main: # gateway组件中的spring-boot-starter-webflux和springboot作为web项目启动必不可少的spring-boot-starter-web出现冲突 web-application-type: reactive application: name: gateway-demo cloud: gateway: nacos-data-id: gateway-routes nacos-group: devops nacos: server-addr: 192.168.3.54:80 username: nacos password: nacos discovery: group: devops namespace: sit config: namespace: sit group: devops config: import: nacos:${spring.application.name}?refresh=true server: port: 8888

3、定义一个component类DynamicRouterLoader,继承ApplicationEventPublisherAware,网上很多教程没有继承,因为版本不同,我用新版的发现必须要用ApplicationEventPublisherAware发布一下才能让路由信息生效。

package com.mj.gateway.router; import cn.hutool.core.lang.Dict; import cn.hutool.json.JSONUtil; import cn.hutool.setting.yaml.YamlUtil; import com.alibaba.cloud.nacos.NacosConfigManager; import com.alibaba.fastjson2.JSONObject; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; 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.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; @Slf4j @Component @RequiredArgsConstructor public class DynamicRouterLoader implements ApplicationEventPublisherAware { //nacos配置管理器 private final NacosConfigManager nacosConfigManager; //gateway路由规则写入器 private final RouteDefinitionWriter writer; private ApplicationEventPublisher publisher; @Value("${spring.cloud.gateway.nacos-data-id:test}") private String dataId; @Value("${spring.cloud.gateway.nacos-group:test}") private String group; private final int timeout = 5000; //保存所有的路由id private final Set<String> routeIds = new HashSet<>(); //PostConstruct表示实例化之后就会执行 @PostConstruct public void initRouteConfigListener() throws NacosException { //获取配置,服务启动的时候就要加载一次配置 String config = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, timeout, new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String config) { //配置变化时,重新加载配置 updateConfig(config); } }); //服务启动,加载配置 updateConfig(config); } private void updateConfig(String config) { log.info("更新路由信息"); //读取nacos中yaml配置 Dict dict = YamlUtil.load(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(config.getBytes())))); //转换成Route对象 List<RouteDefinition> routeDefinitions = JSONObject.parseObject(JSONUtil.toJsonStr(dict)).getJSONObject("spring").getJSONObject("cloud").getJSONObject("gateway").getJSONArray("routes").toList(RouteDefinition.class); //清理旧的路由规则 routeIds.forEach(routeId -> writer.delete(Mono.just(routeId)).subscribe()); //清空缓存 routeIds.clear(); // 遍历添加路由 routeDefinitions.forEach(definition -> { //提交路由信息 writer.save(Mono.just(definition)).subscribe(); //缓存下记录,这样便于清除路由配置 routeIds.add(definition.getId()); }); this.publisher.publishEvent(new RefreshRoutesEvent(this)); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } }

DynamicRouterLoader在服务启动的时候就会实例化并且注入spring中,所以我们给方法initRouteConfigListener添加注解@PostConstruct,这样实例化一完成就会执行该方法。方法里使用NacosConfigManager获取到nacos的数据并做一次更新路由,这次更新是用来启动服务时初始化路由配置,再添加一个监听器,每次nacos修改都会触发这个监听器再次更新路由配置。更新路由其实有两个思路,思路1是能准确判断每次修改、删除的路由信息,然后分别执行update和delete,这就需要我们去做新旧版本配置的区分工作了,工作量比较大,思路2就是每次都清空所有路由,然后把最新版配置信息加载进去。思路2更简单一点,不过我没有测试过路由信息特别大的情况下会不会导致延迟,通常我们的微服务不至于多到会产生延迟,但是这里依旧要警惕,工作中如果发现数量真的很大了,就要多做一些压力测试。

4、重启服务,做测试:http://127.0.0.1:8888/nacos-client-demo/api/login?userid=123&username=zhangsan

这时候是正常的:

5、修改一下nacos上路由信息,把nacos-client-demo的路由直接删掉,保存服务一直正常运行,观察是否生效。

如果debug能发现监听器被调用了,日志也会打印,再调用接口就已经404了。

标签:

SpringCloud系列教程(十二):网关配置动态路由由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“SpringCloud系列教程(十二):网关配置动态路由