一、說明
網(wǎng)關(guān)的核心概念就是路由配置和路由規(guī)則靠闭,而作為所有請求流量的入口帐我,在實際生產(chǎn)環(huán)境中為了保證高可靠和高可用,是盡量要避免重啟的愧膀,所以實現(xiàn)動態(tài)路由是非常有必要的焚刚;本文主要介紹實現(xiàn)的思路,并且以Nacos
為數(shù)據(jù)源來講解
?
二扇调、實現(xiàn)要點
要實現(xiàn)動態(tài)路由只需關(guān)注下面4個點
- 網(wǎng)關(guān)啟動時矿咕,
動態(tài)路由
的數(shù)據(jù)怎樣加載進(jìn)來 -
靜態(tài)路由
與動態(tài)路由
以那個為準(zhǔn),ps:靜態(tài)路由
指的是配置文件里寫死的路由配置 - 監(jiān)聽
動態(tài)路由
的數(shù)據(jù)源變化 - 數(shù)據(jù)有變化時怎樣
通知zuul
刷新路由
?
三狼钮、具體實現(xiàn)
3.1. 實現(xiàn)動態(tài)路由的數(shù)據(jù)加載
- 重寫
SimpleRouteLocator
類的locateRoutes
方法碳柱,此方法是加載路由配置的,父類中是獲取properties中的路由配置熬芜,可以通過擴展此方法莲镣,達(dá)到動態(tài)獲取配置的目的 - 這里采用
靜態(tài)路由
與動態(tài)路由
共存,相同路由id以動態(tài)路由
優(yōu)先覆蓋的實現(xiàn)方式
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中加載靜態(tài)路由信息
routesMap.putAll(super.locateRoutes());
// 從數(shù)據(jù)源中加載動態(tài)路由信息
routesMap.putAll(loadDynamicRoute());
// 優(yōu)化一下配置
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;
}
/**
* 加載路由配置涎拉,由子類去實現(xiàn)
*/
public abstract Map<String, ZuulRoute> loadDynamicRoute();
}
由于動態(tài)路由的數(shù)據(jù)可以有很多種途徑瑞侮,如:Nacos、Redis鼓拧、Zookeeper半火、DB等,所以這里定義一個抽象類季俩,由具體的實現(xiàn)類去定義
loadDynamicRoute
方法
?
3.2. Nacos路由實現(xiàn)類
NacosDynRouteLocator類完整的代碼實現(xiàn)可查看:NacosDynRouteLocator.java
3.2.1. 實現(xiàn)loadDynamicRoute
方法獲取動態(tài)數(shù)據(jù)
@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
監(jiān)聽路由數(shù)據(jù)變化
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);
}
}
注意路由數(shù)據(jù)變化后不需要自己手動刷新路由钮糖,只需要給
zuul
發(fā)送一個RoutesRefreshedEvent
事件即可,zuul
自己有個ZuulRefreshListener類
會監(jiān)聽事件幫我們刷新路由
?
3.3. 配置類創(chuàng)建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實現(xiàn)方式
*/
@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);
}
}
}
這里通過自定義配置來控制是否開啟
動態(tài)路由功能
?
3.4. 添加Nacos
路由配置
新增配置項:
- Data Id:zuul-routes
- Group:ZUUL_GATEWAY
- 配置內(nèi)容:
[
{
"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/"
}
]
添加兩條路由數(shù)據(jù)
?
四酌住、測試
-
啟動網(wǎng)關(guān)通過
/actuator/routes
端點查看當(dāng)前路由信息
file
可以看到靜態(tài)路由和
Nacos
里配置的兩條路由信息并存顯示
-
修改
Nacos
配置店归,關(guān)閉csdn
路由
file -
刷新查看網(wǎng)關(guān)的路由信息
file
csdn
的路由已經(jīng)看不到了韭赘,實現(xiàn)了動態(tài)改變路由配置
?
推薦閱讀
- 日志排查問題困難邀层?分布式日志鏈路跟蹤來幫你
- zuul集成Sentinel最新的網(wǎng)關(guān)流控組件
- 阿里注冊中心Nacos生產(chǎn)部署方案
- Spring Boot自定義配置項在IDE里面實現(xiàn)自動提示
?
請掃碼關(guān)注我的公眾號