Spring Cloud Zuul的動態路由怎樣做?集成Nacos實現很簡單
一、說明
網關的核心概念就是路由配置和路由規則,而作爲所有請求流量的入口,在實際生產環境中爲了保證高可靠和高可用,是儘量要避免重啓的,所以實現動態路由是非常有必要的;本文主要介紹實現的思路,並且以 Nacos
爲數據源來講解
二、實現要點
要實現動態路由只需關注下面4個點
- 網關啓動時,
動態路由
的數據怎樣加載進來 -
靜態路由
與動態路由
以那個爲準,ps:靜態路由
指的是配置文件裏寫死的路由配置 - 監聽
動態路由
的數據源變化 - 數據有變化時怎樣
通知zuul
刷新路由
三、具體實現
3.1. 實現動態路由的數據加載
- 重寫
SimpleRouteLocator
類的locateRoutes
方法,此方法是加載路由配置的,父類中是獲取properties中的路由配置,可以通過擴展此方法,達到動態獲取配置的目的 - 這裏採用
靜態路由
與動態路由
共存,相同路由id以動態路由
優先覆蓋的實現方式
AbstractDynRouteLocator類可查看: AbstractDynRouteLocator.java
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類完整的代碼實現可查看: NacosDynRouteLocator.java
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
的路由已經看不到了,實現了動態改變路由配置
推薦閱讀
請掃碼關注我的公衆號