反向代理 / API 网关 · 一个入口分流到一群后端
匹配引擎讲的是「path → 哪个 handler」。把同一件事搬到服务端最前面那一层,就是 reverse proxy / API gateway:外界只看见一个入口(shop.vega.link),网关拿一张 location 规则表决定这条请求该转发给哪个 upstream(后端服务 / 静态 CDN / SPA 前端)。这正是路由这一层里 dispatch 那一半在生产环境的真实形态。
它和那台平表 matcher 最大的不同在消歧规则:平表是「写在前面的先命中」,而 nginx 这类网关用一套固定优先级——精确 = > 最长前缀 > 正则(按定义顺序),与你把规则写在表里的先后无关。下面输入一条请求,看网关怎样按这套优先级选中一条、再把请求改写后转发给对应 upstream。
那个著名的陷阱:正则会反超前缀。 试试 /static/app.js:前缀 /static/ 明明匹配,命中的却是后面那条正则 ~ \.(js|css|png)$ → CDN。因为 nginx 的优先级里,正则排在普通前缀之前(精确
= 才是最高)。想让前缀「赢回来」,得把它标成优先前缀 ^~——命中后直接采用、根本不再看正则。勾上开关看这一翻转。这是真实运维里极易踩的配置坑。
1 · 选中之后还有一步:改写再转发
匹配只是上半场。命中一条规则后,网关通常还要改写请求再发给 upstream——最常见的是剥掉 location 前缀:外界访问 /api/v2/orders/42,但后端 api-v2 服务自己只认识 /orders/42(它不知道自己被挂在 /api/v2 下)。nginx 里
proxy_pass 末尾带不带斜杠决定剥不剥前缀——又一个一字之差、行为两样的经典点。上面的转发框会显示改写后真正发给后端的 path。
2 · nginx 的 location 表:四种类型、优先级固定
同一张表里,= / ^~ / ~ / 普通前缀四种 location 各有固定档位,与书写先后无关:
server {
listen 443 ssl;
server_name shop.vega.link;
location = /healthz { proxy_pass http://health-check; }
location ^~ /static/ { proxy_pass http://spa-files; }
location ~ \.(js|css|png)$ { proxy_pass http://cdn-static; }
location /api/v2/ { proxy_pass http://api-v2/; }
location /api/ { proxy_pass http://api-v1/; }
location / { proxy_pass http://spa-fallback; }
}
| location | 类型 | 优先级 | 转发行为 |
|---|---|---|---|
= /healthz |
精确 = |
最高,命中即停 | 原样转发给 health-check |
^~ /static/ |
优先前缀 ^~ |
高于正则 | 命中即采用、跳过正则 |
~ \.(js|css|png)$ |
正则 ~ |
高于普通前缀 | 按定义顺序取第一个命中 |
/api/v2/ |
普通前缀 | 取最长匹配 | 末尾带 /,剥掉 /api/v2 再转发 |
/api/ |
普通前缀 | 比 /api/v2/ 短,输给它 |
同样剥前缀转发 |
/ |
普通前缀 | 最短,兜底 | 转发给 spa-fallback |
3 · 网关的匹配内核
网关择优不是「第一条命中」,而是按四档固定优先级择优:
-
精确
=——命中即停,最高。 -
优先前缀
^~——最长那条若带^~,采用并跳过正则。 -
正则
~——按定义顺序取第一个命中(注意:排在普通前缀之前)。 - 普通前缀——回退到最长匹配前缀。
function resolve(rules, path) {
const exact = rules.find(r => r.type === 'exact' && r.loc === path);
if (exact) return exact;
let prefix = null;
for (const r of rules)
if (r.type === 'prefix' && path.startsWith(r.loc))
if (!prefix || r.loc.length > prefix.loc.length) prefix = r;
if (prefix && prefix.caret) return prefix;
const rx = rules.find(r => r.type === 'regex' && new RegExp(r.loc).test(path));
if (rx) return rx;
return prefix;
}
同一台引擎,换了消歧策略与出口。 网关和前端 router 内核一致——都是「一条 path 跑一张规则表」。差别只在两点。消歧:router 多用「顺序即优先级」或 specificity 打分,网关用「精确 > 最长前缀 > 正则」这套固定档位;出口:router 命中后渲染一个视图,网关命中后把请求转发到另一台服务器(并可改写 path / header)。Envoy、Traefik、Kubernetes Ingress 概念相同,只是规则写法(CRD / 标签 / 注解)各异。
4 · 相关页
- 本系列 · 路由匹配引擎——网关是这台 path → handler 引擎搬到服务端最前层的形态;那页讲的「顺序即优先级 / specificity / radix tree」是另一套消歧策略,和这里的「= > prefix > regex」对照看。
- 本系列 · 分享地址统一入口——那页 SPA 接入的三条服务端路由(
/api/*反代 ·/s/:code·*fallback),正是这里 location 表的一个最小实例。 - 本系列 · 加权流量切分——网关选中一个 upstream「组」之后,组里有多个实例——按权重 / 请求头分流到哪个版本,是这条链路的下一环。
- nginx · location 指令——权威定义:
=/^~/~/ 普通前缀四种 location 的匹配优先级与求值顺序——本页演示的就是这套规则。