一、說明 網關的核心概念就是路由配置和路由規則,而作為所有請求流量的入口,在實際生產環境中為了保證高可靠和高可用,是儘量要避免重啟的,所以實現動態路由是非常有必要的;本文主要介紹實現的思路,並且以 為數據源來講解 二、實現要點 要實現動態路由只需關註下麵4個點 1. 網關啟動時, 的數 ...
一、說明
網關的核心概念就是路由配置和路由規則,而作為所有請求流量的入口,在實際生產環境中為了保證高可靠和高可用,是儘量要避免重啟的,所以實現動態路由是非常有必要的;本文主要介紹實現的思路,並且以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
的路由已經看不到了,實現了動態改變路由配置
推薦閱讀
請掃碼關註我的公眾號