文档章节

扩展Zuul实现ignored-patterns的byPass功能

温安适
 温安适
发布于 01/25 23:32
字数 1294
阅读 764
收藏 28

前言

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(注册中心)上的服务如下: image
有2个服务1个api-gateway(即zuul服务),一个hello-service用于测试。 从图上看我的zuul的端口号是5555,尝试访问 localhost:5555/hello-center/hello2Manage/hello

image

但是不使用路由可以访问
image

这说明配置没有生效!

查看源码找办法

回想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地址

总结

无论多小的扩展功能,了解内部原理是必须的无论多小的扩展功能,了解内部原理是必须的 --- 温安适 20190125

© 著作权归作者所有

共有 人打赏支持
温安适
粉丝 107
博文 24
码字总数 39610
作品 0
朝阳
后端工程师
私信 提问
加载中

评论(4)

温安适
温安适

引用来自“freezingsky”的评论

byPass功能大多都是动态增加的,所以,一般都是利用filter业务代码实现.

我的源码可以动态刷新的,欢迎你提供fliter的方式,到时我一定学习
f
freezingsky
byPass功能大多都是动态增加的,所以,一般都是利用filter业务代码实现.
温安适
温安适

引用来自“LiveInPast”的评论

其实不用这么麻烦,想多了,我们这里除了网关的端口对外开放,其他微服务的端口根本不可能对外开放的,不然还要网关干嘛,所有微服务只能通过网关对外提供服务

我公司的单独需求
LiveInPast
LiveInPast
其实不用这么麻烦,想多了,我们这里除了网关的端口对外开放,其他微服务的端口根本不可能对外开放的,不然还要网关干嘛,所有微服务只能通过网关对外提供服务
springCloud(14):使用Zuul构建微服务网关-路由端点与路由配置详解

一、Zuul的路由端点 当@EnableZuulProxy与SpringBoot Actuator配合使用时,Zuul会暴露一个路由管理端点/routes。借助这个端点,可以方便、直观地查看以及管理Zuul的路由。 /routes端点的使用...

我爱大金子
2017/08/22
0
0
springCloud(15):使用Zuul构建微服务网关-Header与文件上传和过滤器

一、Header 1.1、敏感header的设置 一般来说,可在同一个系统中的服务之间共享Header,不过应尽量防止让一些敏感的Header外泄。 zuul: routes: provide-user: sensitive-headers: Cookie,Se...

我爱大金子
2017/08/23
0
0
跟我学Spring Cloud(Finchley版)-17-Zuul路由配置详解

上一节( 跟我学Spring Cloud(Finchley版)-16-Zuul )中,已经实现用Zuul转发到Eureka上的微服务。默认的路由规则是:访问 会被转发到 的 。 但在实际项目中,往往需要自己定义路由规则,Z...

周立_ITMuch
01/27
0
0
springCloud笔记系列(4)-网关配置Zuul

1.SpringCloudZuul是基于Netflix Zuul实现的API网关组件,它实现了请求路由、负载均衡、校验过滤、与服务治理框架的结合、请求转发是的熔断机制和服务的聚合等功能 2.引入依赖 <dependencie...

快乐的小火柴
2018/11/15
0
0
Spring Cloud 入门教程7、服务网关(Zuul)

一、前言 1、什么是服务网关? 服务网关也就是API网关,服务网关可以作为服务的统一入口,提供身份校验、动态路由、负载均衡、安全管理、统计、监控、流量管理、灰度发布、压力测试等功能 服...

吴伟祥
02/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

php register_globals将接收参数转为全局变量

最近在看公司旧的系统的时候发现一个很奇怪的事情,很多页面用的变量找不到源头,没有定义也不是接收,意思是腾空出现的。 经排查,原来是php配置做的好事:register_globals = On。registe...

shikamaru
38分钟前
9
0
Linux 交换分区swap

一、创建和启用swap交换区 如果你的服务器的总是报告内存不足,并且时常因为内存不足而引发服务被强制kill的话,在不增加物理内存的情况下,启用swap交换区作为虚拟内存是一个不错的选择,我...

Yue_Chen
39分钟前
4
0
notepad++如何使用列块编辑模式?

notepad++如何使用列块编辑模式? 听语音 | 浏览:18584 | 更新:2015-12-22 10:56 | 标签:软件 1 2 3 4 5 6 7 分步阅读 notepad++是一款功能强大的文本编辑器,可以支持各种不同的文本类型...

linjin200
41分钟前
1
0
Java 基础语法

一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。 对象:对象是类的一个实例,有状态和行为。例如,一条...

二九结狐六体
45分钟前
3
0
研发团队资源成本优化实践

背景 工程师主要面对的是技术挑战,更关注技术层面的目标。研发团队的管理者则会把实现项目成果和业务需求作为核心目标。实际项目中,研发团队所需资源(比如物理机器、内存、硬盘、网络带宽...

美团技术团队
50分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部