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(注册中心)上的服务以下: api
可是不使用路由能够访问
bash
这说明配置没有生效!架构
回想spring mvc和zuul的知识点,有以下2点,引发了个人注意;mvc
Zuul应该是实现了本身的HandlerMapping?查找源码发现app
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