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服务,并且配置了如下路由规则:


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

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


  
  
  1. zuul.routes.user-service.path=/user-service/** 
  2. zuul.routes.user-service.serviceId=user-service 
  3.  
  4. zuul.routes.user-service-ext.path=/user-service/ext/** 
  5. zuul.routes.user-service-ext.serviceId=user-service-ext 

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

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


  
  
  1. @Override 
  2. public Route getMatchingRoute(final String path) { 
  3.     ... 
  4.     ZuulRoute route = null
  5.     if (!matchesIgnoredPatterns(adjustedPath)) { 
  6.         for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) { 
  7.             String pattern = entry.getKey(); 
  8.             log.debug("Matching pattern:" + pattern); 
  9.             if (this.pathMatcher.match(pattern, adjustedPath)) { 
  10.                 route = entry.getValue(); 
  11.                 break; 
  12.             } 
  13.         } 
  14.     } 
  15.     log.debug("route matched=" + route); 
  16.     return getRoute(route, adjustedPath); 

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


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

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


  
  
  1. zuul: 
  2.   routes: 
  3.     user-service-ext: 
  4.       path: /user-service/ext/** 
  5.       serviceId: user-service-ext 
  6.     user-service: 
  7.       path: /user-service/** 
  8.       serviceId: user-service 

忽略表达式

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

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


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

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


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

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


原文发布时间为:2017-10-18

本文作者:翟永超

本文来自云栖社区合作伙伴“51CTO”,了解相关信息可以关注。

相关文章
|
27天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
55 3
|
2月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
177 6
|
2月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
108 2
|
2月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
31 1
|
3月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
140 5
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
76 0
|
2月前
|
XML Java 数据库连接
【2020Spring编程实战笔记】Spring开发分享~(上)
【2020Spring编程实战笔记】Spring开发分享~
55 0
|
2月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
44 0
|
4月前
|
JSON Java API
解码Spring Boot与JSON的完美融合:提升你的Web开发效率,实战技巧大公开!
【8月更文挑战第29天】Spring Boot作为Java开发的轻量级框架,通过`jackson`库提供了强大的JSON处理功能,简化了Web服务和数据交互的实现。本文通过代码示例介绍如何在Spring Boot中进行JSON序列化和反序列化操作,并展示了处理复杂JSON数据及创建RESTful API的方法,帮助开发者提高效率和应用性能。
187 0
|
3月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba