Zuul- 啟動,我們提到了zuulRefreshRoutesListener,可以用來動態重新整理路由,我們看看他是怎麼載入的。
一下這幾個事件會觸發reset方法。
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent
|| event instanceof RefreshScopeRefreshedEvent
|| event instanceof RoutesRefreshedEvent
|| event instanceof InstanceRegisteredEvent) {
reset();
}
else if (event instanceof ParentHeartbeatEvent) {
ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
else if (event instanceof HeartbeatEvent) {
HeartbeatEvent e = (HeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
}
DiscoveryClientRouteLocator#locateRoutes
追著這個方法下去,我們最後會到DiscoveryClientRouteLocator#locateRoutes方法。這裡主要是獲取兩個地方的配置,一個是我們的配置檔案,一個是Eureka的登錄檔資訊。
protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
// 先呼叫父類的方法,
routesMap.putAll(super.locateRoutes());
if (this.discovery != null) {
Map<String, ZuulRoute> staticServices = new LinkedHashMap<>();
for (ZuulRoute route : routesMap.values()) {
String serviceId = route.getServiceId();
if (serviceId == null) {
serviceId = route.getId();
}
if (serviceId != null) {
staticServices.put(serviceId, route);
}
}
// 從Eureka獲取登錄檔資訊
List<String> services = this.discovery.getServices();
// 是否有過濾的條件
String[] ignored = this.properties.getIgnoredServices()
.toArray(new String[0]);
for (String serviceId : services) {
String key = "/" + mapRouteToService(serviceId) + "/**";
if (staticServices.containsKey(serviceId)
&& staticServices.get(serviceId).getUrl() == null) {
ZuulRoute staticRoute = staticServices.get(serviceId);
if (!StringUtils.hasText(staticRoute.getLocation())) {
staticRoute.setLocation(serviceId);
}
}
if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
&& !routesMap.containsKey(key)) {
routesMap.put(key, new ZuulRoute(key, serviceId));
}
}
}
if (routesMap.get(DEFAULT_ROUTE) != null) {
ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
routesMap.remove(DEFAULT_ROUTE);
routesMap.put(DEFAULT_ROUTE, defaultRoute);
}
// 返回本地配置+Eureka未過濾的登錄檔
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (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;
}
Eureka的登錄檔有個IgnoredServices配置,我們可以對他進行過濾,這樣避免服務名稱直接暴露出來。
zuul:
ignoredServices: '*'
routes:
users: /myusers/**
SimpleRouteLocator#locateRoutes
讀取本地的時候,直接從ZuulProperties取值。
protected Map<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
for (ZuulRoute route : this.properties.getRoutes().values()) {
routesMap.put(route.getPath(), route);
}
return routesMap;
}
ZuulProperties被載入的時候,由於有一個@PostConstruct
註解,他會呼叫init方法。在這裡會根據我們的配置資訊進行賦值serviceId、id、path。
@PostConstruct
public void init() {
for (Entry<String, ZuulRoute> entry : this.routes.entrySet()) {
ZuulRoute value = entry.getValue();
if (!StringUtils.hasText(value.getLocation())) {
value.serviceId = entry.getKey();
}
if (!StringUtils.hasText(value.getId())) {
value.id = entry.getKey();
}
if (!StringUtils.hasText(value.getPath())) {
value.path = "/" + entry.getKey() + "/**";
}
}
}
流程圖
動態路由
我們知道了什麼時候觸發路由的重新載入,那我們可以在路由資訊改變的時候,就呼叫觸發他的條件,比如RoutesRefreshedEvent。
我們知道了他的載入流程,先本地再註冊中心,我們也可以自定義自己的載入流程,比如先本地,再資料庫,資料庫變更,就觸發RoutesRefreshedEvent時間,達到動態路由的目的。