专栏首页程序猿DDSpringCloud实战小贴士:Zuul的路径匹配

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

路径匹配

在之前介绍的 Spring Cloud构建微服务架构:服务网关(路由配置)一文中,我们介绍了如何使用Spring Cloud 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路径。

本文分享自微信公众号 - 程序猿DD(didispace)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-10-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Consul注销实例时候的问题

    当我们在Spring Cloud应用中使用Consul来实现服务治理时,由于Consul不会自动将不可用的服务实例注销掉(deregister),这使得在实际使...

    程序猿DD
  • Spring Cloud第一篇 Eureka简介及原理

    作者:周立 链接:http://itmuch.com/spring-cloud-1/(点击文末阅读原文前往) Eureka是Netflix开发的服务发现组件,本...

    java达人
  • Spring Cloud构建微服务架构:服务消费者

    通过上一篇《Spring Cloud构建微服务架构:服务注册与发现》,我们已经成功地将服务提供者:compute-service服务注册到Eureka服务注册中...

    程序猿DD
  • spring cloud微服务架构新年第一讲(微服务概述)

    什么是微服务 微服务架构是目前开发中的技术热点,出镜率非常高,但是很少人能够完整的理解微服务的定义,以至于现在很多人都在说书技术热点炒作,今天,我就带你...

    用户1257393
  • Spring Cloud第二篇 创建一个Eureka Server

    作者:周立 链接:http://itmuch.com/spring-cloud-2/(点击文末阅读原文前往) 在Spring Cloud实现一个Eureka S...

    java达人
  • Spring Cloud构建微服务架构:服务注册与发现

    在继续编写《Spring Cloud构建微服务架构》系列文章之前,先复刻和重新整理一下之前的内容。除了涵盖原有内容之外,同时做了一些调整,并补充了一些之前缺少的...

    程序猿DD
  • Spring Cloud实战小贴士:版本依赖关系

    去年在博客上连载了《Spring Cloud构建微服务架构》的系列博文,虽然这部分内容得到了不少关注者们的支持,但是不得不说这些内容只是适用于Spring Cl...

    程序猿DD
  • Spring Cloud Zuul实现动态路由

    前言 Zuul 是Netflix 提供的一个开源组件,致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。也有很多公司使用它来作为网关的重要组成部分...

    程序猿DD
  • SpringCloud实战小贴士:随机端口

    太久没有更新,一时不知道该从哪儿继续,索性就从一个小技巧开始吧。 在之前的《Spring Cloud构建微服务架构》系列博文中,我们经常会需要启动多个实例的情况...

    程序猿DD
  • 为Spring Cloud Ribbon配置请求重试【Camden.SR2+】

    当我们使用Spring Cloud Ribbon实现客户端负载均衡的时候,通常都会利用@LoadBalanced来让RestTemplate具备客户端负载功能,...

    程序猿DD

扫码关注云+社区

领取腾讯云代金券