SpringCloud实战小贴士之Zuul的路径匹配

不论是使用传统路由的配置方式还是服务路由的配置方式,我们都需要为每个路由规则定义匹配表达式,也就是上面所说的 path 参数。在Zuul中,路由匹配的路径表达式采用了Ant风格定义。

Ant风格的路径表达式使用起来非常简单,它一共有下面这三种通配符:

通配符 说明
? 匹配任意的单个字符
* 匹配任意数量的字符
** 匹配任意数量的字符,支持多级目录

我们可以通过下表的示例来进一步理解这三个通配符的含义并参考着来使用:

URL路径 说明
/user-service/? 它可以匹配 /user-service/ 之后拼接一个任务字符的路径,比如: /user-service/a 、 /user-service/b 、 /user-service/c
/user-service/* 它可以匹配 /user-service/ 之后拼接任意字符的路径,比如: /user-service/a 、 /user-service/aaa 、 /user-service/bbb 。但是它无法匹配 /user-service/a/b
/user-service/** 它可以匹配 /user-service/* 包含的内容之外,还可以匹配形如 /user-service/a/b 的多级目录路径

另外,当我们使用通配符的时候,经常会碰到这样的问题:一个URL路径可能会被多个不同路由的表达式匹配上。比如:有这样的一个场景,我们在系统建设的一开始实现了 user-service 服务,并且配置了如下路由规则:

zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.serviceId=user-service

但是随着版本的迭代,我们对 user-service 服务做了一些功能拆分,将原属于 user-service 服务的某些功能拆分到了另外一个全新的服务 user-service-ext 中去,而这些拆分的外部调用URL路径希望能够符合规则 /user-service/ext/** ,这个时候我们需要就在配置文件中增加一个路由规则,完整配置如下:

zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.serviceId=user-service

zuul.routes.user-service-ext.path=/user-service/ext/**
zuul.routes.user-service-ext.serviceId=user-service-ext

这个时候,调用 user-service-ext 服务的URL路径实际上会同时被 /user-service/** 和 /user-service/ext/** 两个表达式所匹配。在逻辑上,API网关服务需要优先选择 /user-service/ext/** 路由,然后再匹配 /user-service/** 路由才能实现上述需求。但是如果使用上面的配置方式,实际上是无法保证这样的路由优先顺序的。

从下面的路由匹配算法中,我们可以看到它在使用路由规则匹配请求路径的时候是通过线性遍历的方式,在请求路径获取到第一个匹配的路由规则之后就会返回并结束匹配过程。所以当存在多个匹配的路由规则时,匹配结果完全取决于路由规则的保存顺序。

@Override
public Route getMatchingRoute(final String path){
 ...
 ZuulRoute route = null;
 if (!matchesIgnoredPatterns(adjustedPath)) {
 for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
  String pattern = entry.getKey();
  log.debug("Matching pattern:" + pattern);
  if (this.pathMatcher.match(pattern, adjustedPath)) {
  route = entry.getValue();
  break;
  }
 }
 }
 log.debug("route matched=" + route);
 return getRoute(route, adjustedPath);
}

下面所示代码是基础的路由规则加载算法,我们可以看到这些路由规则是通过 LinkedHashMap 保存的,也就是说路由规则的保存是有序的,而内容的加载是通过遍历配置文件中路由规则依次加入的,所以导致问题的根本原因是对配置文件中内容的读取。

protected Map<String, ZuulRoute> locateRoutes(){
 LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
 for (ZuulRoute route : this.properties.getRoutes().values()) {
 routesMap.put(route.getPath(), route);
 }
 return routesMap;
}

由于 properties 的配置内容无法保证有序,所以当出现这种情况的时候,为了保证路由的优先顺序,我们需要使用YAML文件来配置,以实现有序的路由规则,比如使用下面的定义:

zuul:
routes:
user-service-ext:
path: /user-service/ext/**
serviceId: user-service-ext
user-service:
path: /user-service/**
serviceId: user-service

忽略表达式

通过 path 参数定义的Ant表达式已经能够完成API网关上的路由规则配置功能,但是为了更细粒度和更为灵活的配置路由规则,Zuul还提供了一个忽略表达式参数: zuul.ignored-patterns 。该参数可以用来设置不希望被API网关进行路由的URL表达式。

比如,以快速入门中的示例为基础,如果我们不希望 /hello 接口被路由,那么我们可以这样设置:

zuul.ignored-patterns=/**/hello/**
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=hello-service

然后,可以尝试通过网关来访问 hello-service 的 /hello 接口: http://localhost:5555/api-a/hello 。虽然该访问路径的完全符合 path 参数定义的 /api-a/** 规则,但是由于该路径符合 zuul.ignored-patterns 参数定义的规则,所以不会被正确路由。同时,我们在控制台或日志中还能看到没有匹配路由的输出信息:

o.s.c.n.z.f.pre.PreDecorationFilter   : No route found for uri: /api-a/hello

另外,该参数在使用时还需要注意它的范围并不是对某个路由,而是对所有路由的。所以在设置的时候需要全面的考虑URL规则,防止忽略了不该被忽略的URL路径。

如果您有任何想法或问题需要讨论或交流,可进入交流区发表您的想法或问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring注入Date类型的三种方法总结

    Spring注入Date类型的三种方法总结 测试Bean: public class DateBean { private Date birthday; public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } } 方式1:利用SimpleDateFormat的构造方法注入 <?xml version="1.0&quo

  • 简单了解Spring中常用工具类

    文件资源操作 Spring 定义了一个 org.springframework.core.io.Resource 接口,Resource 接口是为了统一各种类型不同的资源而定义的,Spring 提供了若干 Resource 接口的实现类,这些实现类可以轻松地加载不同类型的底层资源,并提供了获取文件名.URL 地址以及资源内容的操作方法 访问文件资源 * 通过 FileSystemResource 以文件系统绝对路径的方式进行访问: * 通过 ClassPathResource 以类路径的方式进行

  • springboot整合redis进行数据操作(推荐)

    redis是一种常见的nosql,日常开发中,我们使用它的频率比较高,因为它的多种数据接口,很多场景中我们都可以用到,并且redis对分布式这块做的非常好. springboot整合redis比较简单,并且使用redistemplate可以让我们更加方便的对数据进行操作. 1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starte

  • 解决springMVC 跳转js css图片等静态资源无法加载的问题

    web.xml中 servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-va

  • spring boot加载第三方jar包的配置文件的方法

    前言 今天收到一封邮件,大概内容如下:spring boot鼓励去配置化,那么怎么将第三方jar包中的xml去配置化了? 其实,这个问题,在前面的文章中也有提到,http://www.jb51.net/article/125700.htm 下面,我们就以Quartz定时任务为例,单独对这个问题来进行说明,如何实现去配置化. 如果不使用spring boot,我们配置一个简单的定时任务时,需要引入以下配置文件: <!-- 配置需要定时执行的任务类以及方法 --> <bean id=&quo

  • Spring Java-based容器配置详解

    装Java-based的配置 使用 @Import 注解 跟在Spring XML文件中使用<import>元素添加模块化的配置类似,@Import注解允许你加载其他配置类中的@Bean定义: @Configuration public class ConfigA { @Bean public A a() { return new A(); } } @Configuration @Import(ConfigA.class) public class ConfigB { @Bean public

  • 详解SpringBoot 快速整合MyBatis(去XML化)

    序言: 此前,我们主要通过XML来书写SQL和填补对象映射关系.在SpringBoot中我们可以通过注解来快速编写SQL并实现数据访问.(仅需配置:mybatis.configuration.map-underscore-to-camel-case=true).为了方便大家,本案例提供较完整的层次逻辑SpringBoot+MyBatis+Annotation. 具体步骤 1. 引入依赖 在pom.xml 引入ORM框架(Mybaits-Starter)和数据库驱动(MySQL-Conn)的依赖.

  • SpringCloud实战小贴士之Zuul的路径匹配

    不论是使用传统路由的配置方式还是服务路由的配置方式,我们都需要为每个路由规则定义匹配表达式,也就是上面所说的 path 参数.在Zuul中,路由匹配的路径表达式采用了Ant风格定义. Ant风格的路径表达式使用起来非常简单,它一共有下面这三种通配符: 通配符 说明 ? 匹配任意的单个字符 * 匹配任意数量的字符 ** 匹配任意数量的字符,支持多级目录 我们可以通过下表的示例来进一步理解这三个通配符的含义并参考着来使用: URL路径 说明 /user-service/? 它可以匹配 /user-s

  • SpringCloud实战之Zuul网关服务

    为什么需要网关呢? 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先暴露了我们实体机器的IP地址,别人一看你的IP地址就知道服务部署在哪里,让别人很方便的进行攻击操作. 第二,我们这么多服务,我们是不是要挨个调用它呀,我们这里假设做了个权限认证,我们每一个客户访问的都是跑在不同机器上的不同的JVM上的服务程序,我们每一个服务都需要一个服务认证,这样做烦不烦呀,明显是很烦的. 那么我们这时候面临着这两个及其总

  • Java实战小技巧之数组与list互转

    目录 前言 I. 数组转 List 1. Array.asList 1.1 知识点 2. new ArrayList 2.1 避雷预警 3. Collections.addAll II. 列表转数组 III. 小结 总结 前言 这个考题比较常见,也比较简单,难道就这也有什么可以说到的门路不成? 接下来本文好好的说一说它的几种实现姿势,总有一款你喜欢的 I. 数组转 List 1. Array.asList 这个考题太简单了,直接使用Array.asList不就完事了么,比如 @Test publ

  • python实战小游戏之考验记忆力

    导语 哈喽!大家好,我是木木子. 今日游戏更新系列来啦,是不是很想知道今天的游戏是什么类型的?立马安排上-- 随着年纪的不断上升,我们开始丢三落四,忘东忘西,记忆力越来越差了! 这不止大人随着年纪增大记忆力退却,其实很多小孩子也是一样~ 很多家长是不是经常抱怨: "我家孩子背课文特别慢,常常背了几十遍都背不下来,昨晚又背到一点多,我都要崩溃了: 在给孩子辅导课后作业,明明很简单的古诗词填空,孩子的第一反应就是打开书照抄,如果不翻课本,半天写不出来: 昨晚单词背得还好好的,第二天早上抽查的时候,1

  • 每个程序员需掌握的20个代码命名小贴士

    代码中到处都需要命名.作为程序员,我们得给类命名,给变量命名,给函数命名,给参数命名,给命名空间命名,等等等等.下面有20条小贴士能帮助你提高你的命名能力. 1.使用能够表达意图的名字 名字得能告诉我们它要做什么,为什么存在,以及是如何工作的.选择能够表达意图的名字,将更有利于我们理解代码. int d; // elapsed time in days int elapsedTimeInDays; int daysSinceCreation; int daysSinceModification;

  • 使用CDN的优势以及小贴士分享

    什么文件适合用CDN加载 CDN主要适用于一些静态资源文件的加载,比如javascript文件.css样式文件.字体.图片.视频等其他资源文件.这些文件我们往往放到自己的一些前端服务器中做处理(nginx),使用CDN就可以免除前端服务器部分工作了. 使用CDN的好处 提升网站的性能 使用CDN最大的益处是为你的服务器提供"分流",节省了你的带宽,减少了服务器的压力,很多CDN厂商提供的资源访问服务加载速度还是比较快的(墙内除外). 文件缓存 对于那些被普遍使用的资源文件(比如jque

  • 分享Python开发中要注意的十个小贴士

    大家请注意:这篇文中假设我们都用的是Python 3 1. 列表推导式 你有一个list:bag = [1, 2, 3, 4, 5] 现在你想让所有元素翻倍,让它看起来是这个样子: [2, 4, 6, 8, 10] 大多初学者,根据之前语言的经验会大概这样来做 bag = [1, 2, 3, 4, 5] for i in range(len(bag)): bag[i] = bag[i] * 2 但是有更好的方法: bag = [elem * 2 for elem in bag] 很简洁对不对?这

  • js 小贴士一星期合集

    1.今天聊聊自定义事件 事件大家都知道,但在很多的框架中都有自定义事件的实现,我写了个简单的,跟大家分享一下, 复制代码 代码如下: <script> var cusEvent = function(){ var cache = {}; return { addEvent:function(type,fn){ cache[type]?cache[type].push(fn):(cache[type]=[fn]); }, removeEvent:function(type,fn){ if(cac

  • Ajax的小贴士使用小结

    在使用Ajax过程中,有时候总会遇到一些难题,浏览器兼容.编码.IE下的特殊处理等等,偶尔会搞的人头昏脑胀哭笑不得,这里列一些小贴士,或许有些用. 使用Javascript库         Ajax的流行和巨大威力,让我们重新审视了Javascript的开发,也直接促使各种库的出现.对于普通的开发者,使用一些适合自己的Javascript库不仅可以避免Ajax应用上的浏览器兼容等问题,也使其开发更加的稳定和高效.这里列一些我知晓的轻量级的Javascript库:  1 YUI:YAHOO出品,

  • 人尽可用的Windows小贴士之上篇第1/2页

    不管你是否已经升级到最新版Vista系统还是继续留守在XP阵营,我们列出的这些小贴士都能够帮助您加快电脑的处理速度.提升电脑的安全性能,甚至还能够改善电脑的时尚可观性,相信我吧! 客观说来,Windows操作系统的性能可谓一般,如果你不尽力去深度"挖掘",或者按照你的意愿去改变它,那么就永远无法让其发挥出真正的潜力. 无论你是想提高XP的运行速度.自定义Vista的Aero界面.管理自己的磁盘分区或进行快速照片处理,以下我们给出的案例都能悉数告诉你具体的实现方法,在文章中,我们首先会介

随机推荐