一, 说明
网关的核心概念就是路由配置和路由规则, 而作为所有请求流量的入口, 在实际生产环境中为了保证高可靠和高可用, 是尽量要避免重启的, 所以实现动态路由是非常有必要的; 本文主要介绍实现的思路, 并且以 Nacos 为数据源来讲解
二, 实现要点
要实现动态路由只需关注下面 4 个点
网关启动时, 动态路由 的数据怎样加载进来
静态路由 与 动态路由 以那个为准, ps: 静态路由 指的是配置文件里写死的路由配置
监听 动态路由 的数据源变化
数据有变化时怎样 通知 zuul 刷新路由
三, 具体实现
3.1. 实现动态路由的数据加载
重写 SimpleRouteLocator 类的 locateRoutes 方法, 此方法是加载路由配置的, 父类中是获取 properties 中的路由配置, 可以通过扩展此方法, 达到动态获取配置的目的
这里采用 静态路由 与 动态路由 共存, 相同路由 id 以 动态路由 优先覆盖的实现方式
AbstractDynRouteLocator 类可查看:
- public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
- private ZuulProperties properties;
- public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) {
- super(servletPath, properties);
- this.properties = properties;
- }
- /**
- * 刷新路由
- */
- @Override
- public void refresh() {
- doRefresh();
- }
- @Override
- protected Map<String, ZuulRoute> locateRoutes() {
- LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
- // 从 application.properties 中加载静态路由信息
- routesMap.putAll(super.locateRoutes());
- // 从数据源中加载动态路由信息
- routesMap.putAll(loadDynamicRoute());
- // 优化一下配置
- LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
- for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
- String path = entry.getKey();
- // Prepend with slash if not already present.
- if (!path.startsWith("/")) {
- path = "/" + path;
- }
- if (StringUtils.hasText(this.properties.getPrefix())) {
- path = this.properties.getPrefix() + path;
- if (!path.startsWith("/")) {
- path = "/" + path;
- }
- }
- values.put(path, entry.getValue());
- }
- return values;
- }
- /**
- * 加载路由配置, 由子类去实现
- */
- public abstract Map<String, ZuulRoute> loadDynamicRoute();
- }
由于动态路由的数据可以有很多种 途径 , 如: Nacos,Redis,Zookeeper,DB 等, 所以这里定义一个抽象类, 由具体的实现类去定义 loadDynamicRoute 方法
3.2. Nacos 路由实现类
NacosDynRouteLocator 类完整的代码实现可查看:
3.2.1. 实现 loadDynamicRoute 方法获取动态数据
- @Override
- public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
- Map<String, ZuulRoute> routes = new LinkedHashMap<>();
- if (zuulRouteEntities == null) {
- zuulRouteEntities = getNacosConfig();
- }
- for (ZuulRouteEntity result : zuulRouteEntities) {
- if (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) {
- continue;
- }
- ZuulRoute zuulRoute = new ZuulRoute();
- BeanUtil.copyProperties(result, zuulRoute);
- routes.put(zuulRoute.getPath(), zuulRoute);
- }
- return routes;
- }
- private List<ZuulRouteEntity> getNacosConfig() {
- try {
- String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
- return getListByStr(content);
- } catch (NacosException e) {
- log.error("listenerNacos-error", e);
- }
- return new ArrayList<>(0);
- }
3.2.2. 增加 NacosListener 监听路由数据变化
- private void addListener() {
- try {
- nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
- @Override
- public Executor getExecutor() {
- return null;
- }
- @Override
- public void receiveConfigInfo(String configInfo) {
- // 赋值路由信息
- locator.setZuulRouteEntities(getListByStr(configInfo));
- RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
- publisher.publishEvent(routesRefreshedEvent);
- }
- });
- } catch (NacosException e) {
- log.error("nacos-addListener-error", e);
- }
- }
注意路由数据变化后不需要自己手动刷新路由, 只需要给 zuul 发送一个 RoutesRefreshedEvent 事件即可, zuul 自己有个 ZuulRefreshListener 类 会监听事件帮我们刷新路由
3.3. 配置类创建
NacosDynRouteLocator
的 Bean
DynamicZuulRouteConfig 可查看: NacosDynRouteLocator.java
- @Configuration
- @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
- public class DynamicZuulRouteConfig {
- @Autowired
- private ZuulProperties zuulProperties;
- @Autowired
- private DispatcherServletPath dispatcherServletPath;
- /**
- * Nacos 实现方式
- */
- @Configuration
- @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
- public class NacosZuulRoute {
- @Autowired
- private NacosConfigProperties nacosConfigProperties;
- @Autowired
- private ApplicationEventPublisher publisher;
- @Bean
- public NacosDynRouteLocator nacosDynRouteLocator() {
- return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties);
- }
- }
- }
这里通过自定义配置来控制是否开启 动态路由功能
3.4. 添加 Nacos 路由配置
新增配置项:
- Data Id:zuul-routes
- Group:ZUUL_GATEWAY
配置内容:
- [
- {
- "enabled":true,
- "id":"csdn",
- "path":"/csdn/**",
- "retryable":false,
- "stripPrefix":true,
- "url":"https://www.csdn.net/"
- }, {
- "enabled":true,
- "id":"github",
- "path":"/github/**",
- "retryable":false,
- "stripPrefix":true,
- "url":"http://github.com/"
- }
- ]
添加两条路由数据
四, 测试
启动网关通过 /actuator/routes 端点查看当前路由信息
可以看到静态路由和 Nacos 里配置的两条路由信息并存显示
修改 Nacos 配置, 关闭 csdn 路由
刷新查看网关的路由信息
csdn 的路由已经看不到了, 实现了动态改变路由配置
来源: http://www.tuicool.com/articles/vQjEZz3