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

简介: 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路径。

目录
相关文章
|
5月前
|
负载均衡 应用服务中间件 API
微服务技术系列教程(25) - SpringCloud- 接口网关服务Zuul
微服务技术系列教程(25) - SpringCloud- 接口网关服务Zuul
59 0
|
5月前
|
Java API Maven
微服务技术系列教程(27) - SpringCloud- Zuul整合Swagger管理微服务所有API
微服务技术系列教程(27) - SpringCloud- Zuul整合Swagger管理微服务所有API
56 0
|
9月前
|
设计模式 负载均衡 Apache
SpringCloud源码剖析-Zuul使用Ribbon负载均衡-RibbonRoutingFilter
RibbonCommandContext 在run方法中构建了一个 RibbonCommandContext Ribbon的上下文对象,然后调用 forward 方法转发请求 ,通过 setResponse方法设置结果
89 0
|
4月前
|
负载均衡 Java API
SpringCloud - Zuul路由网关使用详解
SpringCloud - Zuul路由网关使用详解
108 0
<8>Springcloud config + zuul 搭建动态网关
把zuul项目当成configClient端,在zuul项目的pom文件中新增依赖
|
7月前
|
安全 Java 测试技术
使用Spring Cloud Zuul实现过滤器或拦截器功能案例
使用Spring Cloud Zuul实现过滤器或拦截器功能案例
186 0
|
9月前
|
负载均衡
SpringCloud源码剖析-Zuul的执行流程
Zuul的执行流程是这样的 首先请求进来会先到达ZuulController ,ZuulController把请求交给ZuulServlet去处理 在ZuulServelt会调用 ZuulRunner 依次执行: init初始化,pre前置filter,route路由filter,post后置filter, error 异常filter ZuulRunner通过 FilterProcessor 去执行各种Filter,FilterProcessor通过 FilterLoader 加载 各种filters 执行完成之后,把结果响应给客户端
53 0
|
9月前
|
负载均衡 Java 微服务
SpringCloud源码剖析-Zuul的自动配置和核心Filter详解
EnableZuulProxy的注释告诉我们,这里设设置Zuul服务器端点,和安装了一些反向代理过滤器,通过这些过滤器它可以转发请求到后端服务器,可以通过配置或通过DiscoveryClient手动注册后端服务(服务发现)
153 0
|
9月前
|
负载均衡 微服务
SpringCloud源码剖析-Zuul的核心Filter
zuul的执行流程 Zuul是服务网关,是微服务的访问入口和出口,它的核心工作思想是:根据客户端的请求URL,通过路由映射到相应的微服务,然后通过服务发现的方式结合Ribbon实现对下游微服务的负载均衡 。Zuul通过大量的Filters实现上述功能,在zuul底层通过ZuulServlet定义整个请求的流程,请求会调用经过pre前置过滤器,route路由过滤器,post后置过滤器,然后返回响应结果,下面是一张摘抄于SpringCloud官网的zuul的生命周期图
75 0