當前位置:
首頁 > 知識 > 擴展Zuul實現ignored-patterns的byPass功能

擴展Zuul實現ignored-patterns的byPass功能

前言

2018年年底的一天,我們的架構師公司如何擴展Zuul時,說了1個功能,如下:

  1. 對zuul的ignoredPath,加入了byPass(旁路功能),可以單獨開放指定的url。 例如:公司屏蔽

/**/*Manage/*,
設置byPassUrl,/**/hello2Manage/*
這時所有滿足/**/hello2Manage/* 都可以被外網訪問。

這個功能我覺得很一般,應該非常的簡單,完成也就10分鐘,結果哎,不說了都是淚啊!


初步設想

zuul 可以跟Eureka結合實現默認配置 zuul可以設置zuul:ignored-patterns 用於設置屏蔽的url, 還可以指定路由配置例如:

zuul:
route: hello-service-ext:
path: /user-service/ext/*
serviceId: user-service-ext

初步想法很簡單,就是在Zuul和Eureka實現默認配置基礎上,加入一個指定路由配置,之後再配置zuul:ignored-patterns為了保證配置按順序生效YAML文件進行配置

初次嘗試的錯誤配置如下

#********* 無效配置 **************

spring:

application:

name: api-gateway

server:

port: 5555

# zuul 開啟特殊的url

# 忽略Mange結尾的數據

zuul:

route:

hello-service-ext:

path: /hello-service/hello2Manage/*

serviceId: hello-service

ignored-patterns: /**/*Manage/*

eureka:

client:

service-url:

defaultZone: http://localhost:1111/eureka

management:

security:

enabled: false


沒看過源碼的情況下,果然的失敗了

本地eureka(註冊中心)上的服務如下:

擴展Zuul實現ignored-patterns的byPass功能

有2個服務1個api-gateway(即zuul服務),一個hello-service用於測試。 從圖上看我的zuul的埠號是5555,嘗試訪問 localhost:5555/hello-center/hello2Manage/hello

擴展Zuul實現ignored-patterns的byPass功能

但是不使用路由可以訪問

擴展Zuul實現ignored-patterns的byPass功能

這說明配置沒有生效!


查看源碼找辦法

回想spring mvc和zuul的知識點,有如下2點,引起了我的注意;

  • spring mvc核心流程中有一步是從HandlerMapping中查找handler,
  • Zuul引入Eureka後,它會為每一個Eureka中的服務自動創建默認路由規則,默認規則為以serviceId配置請求名作為前綴。

Zuul應該是實現了自己的HandlerMapping?查找源碼發現

org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping類,該類實現了MVCHandlerMapping, 將傳入請求路徑映射到遠程服務的

在該類的lookupHandler中,找到了關於 IgnoredPaths的部分,

@Override
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
//這裡有getIgnoredPaths,
//就是獲取配置的zuul:ignored-patterns: /**/*Manage/*
if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
// 忽略部分源碼
}

看到這我突然靈光一現,spring cloud zuul的大神們一定不光在這1個地方進行了IgnoredPaths的判斷,因為不嚴謹,還需要在匹配遠程服務的時候在進行篩選。 順著這個思路我就往下找,註冊Handler的地方,還是在ZuulHandlerMapping中

private void registerHandlers() {
Collection<Route> routes = this.routeLocator.getRoutes();
if (routes.isEmpty()) {
this.logger.warn("No routes found from RouteLocator");
}
else {
for (Route route : routes) {
registerHandler(route.getFullPath(), this.zuul);
}
}
}

可以看出註冊handler,實際上是依賴routeLocator,也就是RouteLocator這個介面的實現的。這個介面的聲明如下:

/**
* @author Dave Syer
*/
public interface RouteLocator {
/**
* 果然有一個Ignored route paths (or patterns), if any.
*/
Collection<String> getIgnoredPaths();
/**
* A map of route path (pattern) to location (e.g. service id or URL).
*/
List<Route> getRoutes();
/**
* Maps a path to an actual route with full metadata.
*/
Route getMatchingRoute(String path);
}

重點關注getMatchingRoute這個方法,因為他是實現路由匹配的規則,裡邊應該有IgnoredPaths 的邏輯。

這個介面,有很多實現,其中2個實現需要關注

  • SimpleRouteLocator

    用於實現Zuul配置文件中聲明的路由關係
  • DiscoveryClientRouteLocator

    是SimpleRouteLocator用於實現從Eureka中默認配置路由關係

我仔細查看了SimpleRouteLocator類發現如下邏輯,果然有個matchesIgnoredPatterns方法用於過濾url,DiscoveryClientRouteLocator並沒有重寫這個方法。

public Route getMatchingRoute(final String path) {
return getSimpleMatchingRoute(path);
}
protected Route getSimpleMatchingRoute(final String path) {
//省略部分代碼
String adjustedPath = adjustPath(path);
//獲取url對應的路由信息
ZuulRoute route = getZuulRoute(adjustedPath);
return getRoute(route, adjustedPath);
}
protected ZuulRoute getZuulRoute(String adjustedPath) {
// 果然有個校驗,IgnoredPath的地方
if (!matchesIgnoredPatterns(adjustedPath)) {
//省略部分源碼
}
return null;
}
protected boolean matchesIgnoredPatterns(String path) {
for (String pattern : this.properties.getIgnoredPatterns()) {
log.debug("Matching ignored pattern:" + pattern);
if (this.pathMatcher.match(pattern, path)) {
log.debug("Path " + path + " matches ignored pattern " + pattern);
return true;
}
}
return false;
}

擴展源碼注意事項

進過上述分析,要實現對ignoredPath的byPass(旁路功能),需要擴展3個類

  • ZuulHandlerMapping,重寫lookupHandler方法
  • SimpleRouteLocator,重寫matchesIgnoredPatterns方法
  • DiscoveryClientRouteLocator,重寫matchesIgnoredPatterns方法

因為實際擴展很簡單擴展的部分可以,到碼雲我提供的源碼獲取。不在這贅述

擴展之後,還是需要將其注入到Spring中。我們擴展ZuulProxyAutoConfiguration,擴展方式如下:

@Configuration
public class ZuulProxyConfigurationExtend extends ZuulProxyAutoConfiguration {
@Autowired
ZuulConstantReload zuulConstantReload;
@Autowired
private ErrorController errorController;
@Autowired
private DiscoveryClient discovery;
@Autowired
private ServiceRouteMapper serviceRouteMapper;
@Autowired(required = false)
private Registration registration;
@RefreshScope
@ConfigurationProperties("zuul")
@Primary
@Bean
public ZuulProperties zuulProperties() {
return new ZuulProperties();
}
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
ZuulHandlerMapping mapping = new ZuulHandlerMappingExtend(routes, zuulController());
mapping.setErrorController(this.errorController);
return mapping;
}
@Bean
public SimpleRouteLocator simpleRouteLocator() {
SimpleRouteLocatorExtend simpleRouteLocator= new SimpleRouteLocatorExtend(this.server.getServletPrefix(), this.zuulProperties);
return simpleRouteLocator;
}
@Bean
public DiscoveryClientRouteLocator discoveryRouteLocator() {
DiscoveryClientRouteLocatorExtend discoveryClientRouteLocatorExtend= new DiscoveryClientRouteLocatorExtend(this.server.getServletPrefix(),
this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
return discoveryClientRouteLocatorExtend;
}
}

對應配置與其他擴展類,傳送門

開源中國碼雲地址

github地址


總結

無論多小的擴展功能,了解內部原理是必須的無論多小的擴展功能,了解內部原理是必須的

{!-- PGC_COLUMN --}

? 著作權歸作者所有

作者:溫安適

原文:https://my.oschina.net/floor/blog/3006144

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

關於vs code文本編輯器的快捷鍵
SQL語法和C井調用

TAG:程序員小新人學習 |