探究URL路径参数在Kong与Spring中的实现机理

56 阅读3分钟

前提

在工作中遇到一个问题,基于kong的API网关,在遇到某些含有路径参数的情况下,会发生问题。下面举个例子,以说明情况。

当前有待注册服务请求A:/path/{pageNo}/{pageSize},请求B:/path/{id}/10,它们都是以Spring实现的服务。当将服务注册到API网关,且请求路径保持一致,当调用API网关的请求时,则可能出现访问混淆的问题。比如,如果访问请求为/path/1/10,则不清楚会匹配哪个路径,而直接访问spring服务(此时充当API网关的上游服务),则会直接匹配/path/{id}/10

探究

kong

首先,我们需要了解kong的路径匹配规则。由于基于Kong的API网关在描述上述参数化路径,将其转化为对应的正则匹配路径:

  • 请求A: /path/[^\]\[^\]
  • 请求B: /path/[^\]/10

而面对同是正则表达式的路径,匹配将以其出现在配置中的顺序区别优先级,即先出现的正则表达式,优先级高。

这也就能够解释为什么访问API网关时,会出现请求混淆的问题。

那么,如何解决这个问题呢?

其实,Kong是提供了解决办法的。我们可以在Kong的Route实体资源中设置regex_priority而区分不同正则匹配路由的优先级,设置的值越大,优先级越高。

下面是一个简单解决方案,通过查看参数路径中的参数个数,如果个数越多,相应的优先级越低。

spring

那么,为什么spring并没有出现此类问题呢?实际上,在spring的实现中,依然采取了在kong中我们使用的方案,而该实现类就是AntPathMatcher,当然它的实现方案更为复杂,考虑的内容也更为全面。下面内容,并不是对于该核心类的解析,而是叙述一下在探究该问题过程中,调试spring源码的经过。

首先,是找到调试的入口类FrameworkServlet,可以在其中的service()方法打下断点。

然后,进入DispatcherServlet类,可以在其doDispatch方法中打下断点。但该类中,更为关键的方法是getHandler(),此时,分发器将为URL寻找Handler。

接着,我们进入AbstractHandlerMethodMapping类,查找方法getHandlerInternal()lookupHandlerMethod()。此时,我们已经触及真相的边缘。当阅读方法中的代码,我们可以看到当我们访问/path/1/10时,lookupHandlerMethod方法中的matches将会有两个。而我们知道,多个匹配对于spring来说是不允许的,它需要能够确定一个最佳匹配。这时候会用到一个比较器,而该比较器的核心就是AntPathMatcher,具体的比较内容可以查看该类的compare方法。

其实,这里以时序图说明会更好些。

结论

我们可以看到,其实spring与kong都提供了面向参数化路径的解决方法,而且有异曲同工之妙。其实核心,就是对于多个满足的匹配路径,能够排出顺序,以满足最佳匹配。