0%

聊一聊网关路由的优先级

今天聊一个小众的专业话题,L7 网关路由的优先级。

(虽然是个小的点,最后呢,我们也希望以小窥大,从历史的演进中,咂摸出一些道理)

以接入层为例,www.test.com 这一个域名上,可能有多个路由,比如以 /user/ 为前缀的请求,路由到后端的用户服务,以 /order/ 为前缀的请求,路由到后端的订单服务。

很显然,这两条路由规则,是没有冲突的,但是,如果是 /user//user/login/ 这两个前缀呢?那就必须有一套优先级机制。

接下来,看看大家都是怎么玩的,最后,我们再来对比分析。

NGINX

首先,我们看看老派的 NGINX

NGINX 的路由规则,是以 nginx conf 中的 location 配置来表达的,而 NGINX 的 location 匹配优先级,还是比较复杂的,很多用了几年的 NGINX 玩家,也不一定搞得清楚。

汗,作为一个玩了十来年的老炮,我也是搜索才找到的这份规则(这两年没玩了,就忘了,其实以前主要玩 OpenResty,用 NGINX 这套的机会也不多)

1
2
3
4
5
6
location = /uri       # 精确匹配
location ^~ /uri # 前缀匹配,并且在正则之前
location ~ pattern # 区分大小写的正则匹配
location ~* pattern # 不区分大小写的正则匹配
location /uri # 前缀匹配,但是在正则匹配之后
location / # 通用匹配

NGINX 的优先级,是按照:

  1. 精确匹配
  2. ^~ 前缀匹配,前缀越长优先级越高
  3. 正则匹配,按照在配置文件的书写顺序
  4. 普通前缀匹配,还是长度优先
  5. 通用匹配

Envoy

作为云原生时代的后起之秀,Envoy 的处理策略就简单多了,直接甩锅给控制面。

Envoy 作为数据面,只是按照 rds 配置中的路由顺序,来顺序执行。

对,就是这么简单粗暴。

Istio

那么,Envoy 的控制面搭档 Istio 是如何接锅的呢?很遗憾,至少 Istio 定义的 VirtualService 没有接锅。

按照 VirtualService 的推荐玩法,同一个域名的路由,推荐是合并到写到一个 VirtualService 资源,也就是继续甩锅给上层。

谁是上层,写 VirtualService 的那就是人类用户,或者更上层的 console 咯。

如果将同域名的路由写入到不同的 VirtualService,Istio 倒也是会做合并操作(至少 Gateway 场景的 VirtualService 是有合并的),但是呢,优先级排序是基本不管了,唯一的处理是,把通配的路由放到最后。

k8s Gateway API

Istio 的 VirtualService 是继续甩锅,我们继续看看新一代标准,k8s Gateway API

终于,这次没有甩锅,而是做了相对明确的优先级约定:

  1. 精确匹配
  2. 前缀匹配,前缀越长优先级越高
  3. 请求方法匹配
  4. 请求头匹配,数量大的优先
  5. 请求参数匹配,数量大的优先

不过,对于比较复杂的正则匹配,也没有做约定,明确甩锅给实现者了

Ingress

最后,我们看看上一代的 k8s 标准,Ingress。

Ingress 明确约定的匹配方式,只有 URI 精确匹配,和 URI 前缀匹配。

虽然我没有找到明确的优先级规定,但是,这两个的优先级顺序,是有比较强共识,没有啥歧义的。

并且,上一代 Ingress 标准,主要以 NGINX 为数据面,所以,Ingress 路由的优先级,大概率也是跟着 NGINX 走了。

个人点评

个人观点,难免有些偏颇,如有不同意见,欢迎拍砖

NGINX

总体来说,NGINX 的路由优先级是比较完备的,综合考虑到了各种场景(不愧是老牌王者)

比如前后端还没有分离清楚的应用,就会有 /user/ 这种前缀匹配,还有 *.js 这种后缀匹配(也就是正则匹配),那么前缀匹配和正则匹配,也需要有优先级的约定了。

并且 NGINX 还有两种前缀匹配,可以在正则前,也可以在正则后,还是比较完备的了。

但是呢,还是有点复杂的,没有仔细看过,还挺容易踩坑。

Envoy

反观 Envoy 这个后期之秀,就是甩锅好手了,哈哈。

这里的甩锅并非贬义,在云原生的时代,多了一层独立的资源抽象层,底层的简单粗暴,倒也是给上层留下的足够的灵活性,这么做也有相当的合理性。

Istio

不过,Istio 这锅甩得嘛,作为站着说话不腰疼的人来说,可以吐槽这是 Istio 不作为。

但是呢,Istio 也是精力有限,主要投入在 Service Mesh 这块新生市场,而对于 Mesh 场景,一般都没这么些个复杂的路由规则。

k8s Gateway API

好在 k8s Gateway API 顺利接上了这个锅,随着 Istio 对于 k8s Gateway API 支持的跟进,Istio 也算接上了这个锅。

对于 Gateway API,我的理解,就是 Envoy/Istio 这批新生代,在奠定了 Mesh 的主导地位之后,杀向 Ingress 接入场景的冲锋号。所以,在 Gateway API 的标准里,我们能看到 Envoy/Istio 背后技术理念的呈现,以及,背后与 NGINX 派的折中/妥协。

总体来说,我觉得是好事,只有大家坐下来,一起构建统一标准 API 的时候,云原生的技术红利才能更大程度的普及。

Ingress

至于 Ingress,我觉得很像是,在早期 k8s 构建完整版图里,快速推上去的一个方案,有很多的局限性,迟早是要被淘汰的。

不过,应该也不会很快,至少从现在 k8s Gateway API 的推进速度来看,估计还有个两三年才会形成市场趋势(技术趋势其实是很明显的了)。

最后

抽象成标准化资源,是云原生很核心的一点,而然,标准的形成,非一朝一夕之功。

要推进行业级共识,需要这份标准有足够强的适用性,覆盖足够多的场景。而对于,标准制定者/发起人,则需要对行业应用场景有足够的洞见,对上下游环境,以及未来发展趋势也有足够精准的理解。

k8s Gateway API 未来要走的路,依然很长,很多前人(Ingress,Istio CRD)做得不够好的地方,确实看到一些改进。不过,从我个人的角度看,还只是摘了一些比较低垂的果实,更多是沉淀了一些先人的成功经验,还有很多硬骨头要啃。

如果对这块感兴趣的,欢迎一起交流~