2018年年末的一天,咱們的架構師公司如何擴展Zuul時,說了1個功能,以下:java
/**/*Manage/*, 設置byPassUrl,/**/hello2Manage/* 這時全部知足/**/hello2Manage/* 均可以被外網訪問。
這個功能我以爲很通常,應該很是的簡單,完成也就10分鐘,結果哎,不說了都是淚啊!git
zuul 能夠跟Eureka結合實現默認配置 zuul能夠設置zuul:ignored-patterns 用於設置屏蔽的url, 還能夠指定路由配置例如:github
zuul: route: hello-service-ext: path: /user-service/ext/* serviceId: user-service-ext
初步想法很簡單,就是在Zuul和Eureka實現默認配置基礎上,加入一個指定路由配置,以後再配置zuul:ignored-patterns ,爲了保證配置按順序生效YAML文件進行配置。web
初次嘗試的錯誤配置以下:spring
#********* 無效配置 ************** 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(註冊中心)上的服務以下:
有2個服務1個api-gateway(即zuul服務),一個hello-service用於測試。 從圖上看個人zuul的端口號是5555,嘗試訪問 localhost:5555/hello-center/hello2Manage/helloapi
可是不使用路由能夠訪問
架構
這說明配置沒有生效!mvc
回想spring mvc和zuul的知識點,有以下2點,引發了個人注意;app
Zuul應該是實現了本身的HandlerMapping?查找源碼發現ide
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類發現以下邏輯,果真有個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個類
由於實際擴展很簡單擴展的部分能夠,到碼雲我提供的源碼獲取。不在這贅述 。
擴展以後,仍是須要將其注入到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; } }
不管多小的擴展功能,瞭解內部原理是必須的 --- 溫安適 20190125