内容简介:2018年年底的一天,我们的架构师公司如何扩展Zuul时,说了1个功能,如下:这个功能我觉得很一般,应该非常的简单,完成也就10分钟,结果哎,不说了都是泪啊!zuul 可以跟Eureka结合实现默认配置 zuul可以设置
前言
2018年年底的一天,我们的架构师公司如何扩展Zuul时,说了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(注册中心)上的服务如下: 有2个服务1个api-gateway(即zuul服务),一个hello-service用于测试。 从图上看我的zuul的端口号是5555,尝试访问 localhost:5555/hello-center/hello2Manage/hello
但是不使用路由可以访问
这说明配置没有生效!
查看源码找办法
回想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; } }
对应配置与其他扩展类,传送门
总结
无论多小的扩展功能,了解内部原理是必须的无论多小的扩展功能,了解内部原理是必须的 --- 温安适 20190125
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 优秀开源框架的扩展机制实现
- 关于接口的实现和扩展的思考
- 图解kubernetes资源扩展机制实现(上)
- flask通过配置文件实现程序可扩展
- 扩展Spring Cloud Feign 实现自动降级
- Sentinel Slot 扩展实践:流控熔断预警实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。