网关要支持地域、用户等级这类高级路由,核心是把写死的转发逻辑改造成「特征提取 + 条件匹配 + 目标决策」三段式规则引擎,规则存配置中心、运行时热更新。我们的企业级 AI 网关最初只有路径与 Header 匹配,后来按这个思路扩展出基于地域、基于用户等级、基于灰度标签三类策略,新增策略不需要改代码、不重启网关,改一条配置就能生效。下面给出完整的设计与可复用实现。
一、路由策略的抽象:三段式规则引擎
先把路由请求的生命周期拆成三段,后续所有策略都复用这套骨架:
- 特征提取:从请求里抽路径、Header、Cookie、源 IP、登录态等信息,组装成统一的路由上下文;
- 条件匹配:按规则集合逐条求值,规则支持与或组合,命中即返回目标;
- 目标决策:把请求转发到对应后端实例或模型,记录命中规则用于审计。
规则本体用结构化配置描述,存到 Redis,网关订阅变更即可热更新,不重启。路由上下文用不可变对象承载,避免并发写坏:
public record RouteContext(
String path, String method,
String clientIp, String region,
String userId, int userLevel,
String versionTag, Map<String, String> headers) {
public static RouteContext from(HttpServletRequest request, JwtPayload jwt) {
return new RouteContext(
request.getRequestURI(), request.getMethod(),
resolveClientIp(request), resolveRegion(request),
jwt.userId(), jwt.level(),
jwt.tag(), copyHeaders(request));
}
}
规则匹配器只需做一件事:上下文满足全部条件才放行。
二、基于地域的路由:IP 解析与就近转发
地域路由要解决两个问题:拿到客户端真实 IP,再把 IP 映射到区域。真实 IP 在代理层逐级透传,网关从 X-Forwarded-For 取最左端合法地址;IP 到区域的映射用离线 GeoIP 库,库文件随网关发布,不依赖外部接口,避免每次请求都做远程查询。
区域信息在进入规则引擎前就解析好,放进路由上下文的 region 字段。规则配置里写清楚区域与目标实例的对应关系:
routing:
rules:
- id: region-cn
priority: 10
conditions:
- region in [cn-north, cn-east]
target: upstream-cn
- id: region-overseas
priority: 10
conditions:
- region in [ap-southeast, us-west]
target: upstream-overseas
后端实例在注册中心按区域打标签,网关匹配到目标组后再从同区域实例里做负载均衡,实现就近访问。注意 X-Forwarded-For 可伪造,线上环境配合可信代理白名单使用,内网与测试流量单独走一条链路,防止地域策略被绕过。
三、基于用户等级的路由:登录态解出等级再分流
用户等级路由的前提是网关能拿到用户身份。登录态在认证阶段已经解析成 JWT 并校验,这里直接复用其载荷里的等级字段,按等级把请求分到不同规格的后端。典型场景是付费用户走高规格模型实例、免费用户走通用实例:
| 路由维度 | 特征来源 | 典型用途 | 实现成本 |
|---|---|---|---|
| 路径匹配 | 请求 URI | 基础分发 | 低 |
| 地域路由 | 源 IP + GeoIP | 就近访问、区域合规 | 中 |
| 用户等级 | JWT / Header | 付费分流、体验分级 | 中 |
| 灰度标签 | Header / Cookie | 新版本小流量验证 | 低 |
| 权重分配 | 无,随机按比例 | 容量控制、流量切分 | 低 |
等级判断放在认证过滤器之后、转发过滤器之前,判定结果写进路由上下文,规则引擎直接读取,不再二次解析。等级路由与地域路由可以叠加:规则用 AND 组合「用户等级 + 区域」两个条件,先过滤用户再看区域,实现付费用户即使跨区域也走专属实例。
四、规则热更新与优先级
规则多了之后,匹配顺序必须显式定义。每条规则带 priority 字段,数字小的先匹配,命中即停,不再向下试探。规则变更走配置中心发布,网关监听变更事件后重建内存路由表:
@Component
public class RuleStore {
private volatile List<RouteRule> rules = List.of();
@EventListener
public void onConfigRefresh(ConfigRefreshEvent event) {
this.rules = loadRulesFromRedis();
log.info("路由规则已热更新,当前 {} 条", rules.size());
}
public RouteRule match(RouteContext ctx) {
return rules.stream()
.filter(r -> r.matches(ctx))
.findFirst()
.orElse(defaultRule);
}
}
热更新期间用 volatile 引用切换新旧规则表,单次请求要么全用新表、要么全用旧表,不会出现半套规则。同时把每一条命中的规则 ID 记录到访问日志,排障时能还原请求实际走了哪条规则。
五、落地时的三个注意点
规则引擎越灵活,误配风险越高。三层防护必须跟上:规则发布前先跑语法校验与规则冲突检测,同一请求命中多条同优先级规则直接报错拦截;灰度规则限定小流量比例,配合监控观察错误率再放量;关键规则做 AB 对比,用同一批请求分别走新旧规则验证结果一致后再全量切换。地域路由涉及的 IP 库要定期更新,否则新上线机房的流量会被错误归类。
六、路由策略在 AI 网关中的实际效果
这套引擎在我们的 AI 网关里承担两类分流:免费用户与付费用户分别路由到不同规格的模型实例,付费用户固定走长文创作能力更强的高规格模型;按区域把请求分流到就近集群,海外用户的对话首包延迟从跨区绕行降到本区直连。模型实例在注册中心按 level 与 region 两个维度打标签,规则引擎匹配目标后按标签筛选实例再做负载均衡。上线以来路由规则做过三轮热更新,未重启一次服务,规则命中率通过访问日志里的规则 ID 逐条核对,与预期配置完全一致。
常见问题(FAQ)
Q1:地域路由识别不准怎么办?
优先信任 X-Forwarded-For 并过滤伪造地址,定期更新 GeoIP 库,运营商级出口 IP 允许按段兜底。
Q2:多条件规则怎么保证不会冲突?
每条规则定义 priority,同优先级内命中多条则视为配置错误并拦截,发布前做冲突校验。
Q3:规则热更新需要重启网关吗?
不需要。规则存配置中心,网关订阅变更重建内存路由表,volatile 引用保证切换原子。