☰
BFE 请求 body 条件原语详解:基于 JSON 字段与 Content-Length 的智能路由
2026/10/10 2:13:26 网站建设 项目流程
  • 后端
  • 网络/通信
  • 云原生

【免费下载链接】bfe

A modern layer 7 load balancer from baidu

项目地址:https://gitcode.com/gh_mirrors/bf/bfe
点击查看免费下载

BFE(Baidu Front End)作为一款现代化的七层负载均衡器,在 条件系统 中内置了一组针对请求 body 的匹配原语,用于在路由阶段直接读取请求体内容或请求体大小并做出决策。本文围绕 body 原语参考文档 展开,逐一讲解req_body_json_in、req_body_json_prefix_in、req_body_larger_than、req_body_less_than四个原语的语义、参数、实现原理与实战用法,并结合源码与集成测试给出可直接落地的配置示例。读完本文,你将掌握在 AI 网关按模型字段路由、按 Prompt/请求体大小分流等典型场景下编写精确条件表达式的方法。

条件原语在 BFE 中的工作方式

BFE 的条件表达式由bfe_basic/condition包负责解析与求值。每个原语在 build.go 中注册,遵循统一的分工模式:Fetcher(取数) + Matcher(匹配)。

以 PrimitiveCond 的求值逻辑为例:

func (p *PrimitiveCond) Match(req *bfe_basic.Request) bool { if req == nil || req.Session == nil || req.HttpRequest == nil { return false } fetched, err := p.fetcher.Fetch(req) if err != nil { return false } r := p.matcher.Match(fetched) return r }

也就是说:Fetcher 从请求中提取出某个值(如 JSON 字段值、Content-Length),Matcher 判断该值是否满足条件;任一环节出错(例如取不到值)都会安全地返回false,而不会导致路由异常。语法层面,parser/semant.go 规定了各原语的参数类型:

"req_body_json_in": []Token{STRING, STRING, BOOL}, "req_body_json_prefix_in": []Token{STRING, STRING, BOOL}, "req_body_larger_than": []Token{INT}, "req_body_less_than": []Token{INT},

两个 JSON 类原语接收两个字符串加一个布尔值,两个大小类原语接收一个整数,参数不符会在构建期直接报错。

req_body_json_in:JSON 字段精确匹配

语义与参数

req_body_json_in(json_path, value_list, case_insensitive)的含义是:在 JSON 格式的请求 body 中,查找json_path指定的字段,判断其值是否精确匹配value_list中的某一项。

参数描述
json_pathString,请求 body 中 JSON 字段的路径,支持点号嵌套(如data.model)
value_listString,value 列表,多个取值之间使用|连接
case_insensitiveBoolean,是否忽略大小写

示例:

req_body_json_in("model", "deepseek-r1|qwen-plus", true)

该表达式表示:当请求 body 中model字段的值(忽略大小写)为deepseek-r1或qwen-plus之一时命中。

实现原理

在 build.go 中,该原语被构建为:

case "req_body_json_in": return &PrimitiveCond{ name: node.Fun.Name, node: node, fetcher: &ReqBodyJsonFetcher{ path: node.Args[0].Value, }, matcher: NewInMatcher(node.Args[1].Value, node.Args[2].ToBool()), }, nil
  • Fetcher:ReqBodyJsonFetcher(primitive.go)按path从请求 body 中提取字段值,最终由HttpReqBodyJsonGet调用gjson.GetBytes(body, path)完成 JSON 路径解析,详见 primitive.go。字段不存在时返回空字符串(不视为错误),因此不会误报异常。
  • Matcher:NewInMatcher(primitive.go)将value_list按|切分为模式数组;若case_insensitive为true,则先将模式和待匹配值统一转为大写再做精确比较,匹配逻辑见 InMatcher.Match。
  • 性能细节:ReqBodyJsonFetch使用请求上下文缓存(key 为jsoncache.前缀 + path),同一请求中对同一路径的多次取值只解析一次,详见 primitive.go。

使用要点

  • 匹配是精确匹配,字段值必须与列表中某一项完全相同(忽略大小写与否由第三个参数决定);
  • json_path支持gjson的路径语法,嵌套字段可写为a.b.c形式;
  • 适合精确枚举模型名、状态码、类型等取值有限的场景。

req_body_json_prefix_in:JSON 字段前缀匹配

语义与参数

req_body_json_prefix_in(json_path, value_prefix_list, case_insensitive)的含义是:在 JSON 格式的请求 body 中,查找json_path指定的字段,判断其字符串值是否以value_prefix_list中的某一项为前缀。

参数描述
json_pathString,请求 body 中 JSON 字段的路径
value_prefix_listString,前缀列表,多个之间使用|连接
case_insensitiveBoolean,是否忽略大小写

示例:

// 命中所有 OpenRouter 模型 req_body_json_prefix_in("model", "openrouter/", false) // 命中 OpenRouter 下 anthropic 子命名空间的所有模型 req_body_json_prefix_in("model", "openrouter/anthropic/", false) // 命中所有 gpt- 或 claude- 开头的模型(大小写不敏感) req_body_json_prefix_in("model", "gpt-|claude-", true)

实现原理

该原语的构建方式与req_body_json_in完全相同,仅 Matcher 换成NewPrefixInMatcher,见 build.go。PrefixInMatcher的实现位于 primitive.go:先将前缀列表按|切分,忽略大小写时统一转为大写,随后对字段值做前缀判断(prefixIn)。

从 primitive_test.go 中的测试用例可以看出其行为边界:

  • {"model":"openrouter/anthropic/claude-sonnet-4.6"}命中前缀openrouter/;
  • {"model":"openrouter"}不命中(前缀带/,要求值以openrouter/开头);
  • {}不命中(字段不存在);
  • gpt-|claude-双前缀可同时匹配gpt-4与claude-3-opus。

使用要点

  • 前缀匹配非常适合模型命名空间类路由:模型 ID 常带有供应商/命名空间前缀(如openrouter/anthropic/...),用前缀可以一键覆盖整个命名空间下的所有模型,无需维护冗长的精确名单;
  • 注意前缀是带边界的字符串前缀,openrouter/不会命中值恰好为openrouter的字段;
  • 可与req_body_json_in组合使用,例如同时判断精确值与前缀,测试见 TestReqBodyJsonPrefixInCombineWithIn。

req_body_larger_than / req_body_less_than:按请求体大小路由

语义与参数

req_body_larger_than(size)判断请求头Content-Length的值是否严格大于size(单位:字节);req_body_less_than(size)则判断是否严格小于size。

参数描述
sizeInteger,字节数阈值

示例:

// 请求体大于 8KB 时命中 req_body_larger_than(8192) // 请求体小于 2KB 时命中 req_body_less_than(2048)

设计背景:AI 网关按 Prompt 长度路由

这两个原语源于 prompt 长度路由设计文档:AI 网关客户希望根据输入 prompt 长度执行路由策略——长文本请求路由到擅长长上下文的集群/模型,短文本请求路由到成本更低、响应更快的集群/模型。由于 BFE 对 AI 请求采用流式转发,转发开始时通常无法确定完整 body 大小,直接解析 body 计算 prompt 长度会受AccessibleBodySize限制且开销较大,因此设计上直接利用 HTTPContent-Length头作为请求体字节数的代理指标。

实现原理

两个原语在 build.go 中注册:

case "req_body_larger_than": size, err := strconv.ParseInt(node.Args[0].Value, 10, 64) if err != nil { return nil, fmt.Errorf("req_body_larger_than: invalid size %s", node.Args[0].Value) } if size < 0 { return nil, fmt.Errorf("req_body_larger_than: size should not be negative") } return &PrimitiveCond{ name: node.Fun.Name, node: node, fetcher: &ContentLengthFetcher{}, matcher: &GtInt64Matcher{threshold: size}, }, nil
  • Fetcher:ContentLengthFetcher从req.HttpRequest.Header读取Content-Length头并解析为 int64,见 primitive.go。头部缺失或格式非法(如abc)都会返回错误。
  • Matcher:GtInt64Matcher实现n > threshold,LtInt64Matcher实现n < threshold,均为严格比较(等于阈值不命中),见 primitive.go。
  • 构建期校验:size 必须能解析为整数且不能为负,否则构建条件时即报错。

单元测试 TestContentLengthFetcher、TestGtInt64Matcher、TestLtInt64Matcher 覆盖了缺失头、非法头、边界值(100 对>100与<100均不命中)等场景。

关键注意事项

  • 数据来源是Content-Length头:反映的是整个 HTTP body 的字节数,不是纯 prompt 文本长度——JSON 请求中字段名、引号、空白等结构开销都会被计入;
  • 无Content-Length头时返回false:例如 chunked(分块传输)请求,两个原语均不命中,行为明确且安全,不会误路由;
  • 阈值校准:配置时建议通过实际请求采样校准,预留 JSON 结构本身的固定开销。设计文档中的原话是"实际 prompt 文本会略小于此值(扣除 JSON 字段开销)",详见 design-changes.md;
  • 两个原语可与其他原语组合,例如req_host_in(...) && req_body_larger_than(8192)。

实战:在 mod_ai_route 中配置 body 条件路由

条件原语最常见的落地场景是 AI 网关模块mod_ai_route的路由规则表。以下配置取自 SC04 集成测试数据,演示按模型前缀路由:

{ "Version": "20260720150000", "route_rules": { "apikey_ak_user_a": { "type": "apikey", "owner": "ak_user_a", "rules": [ { "name": "user_a-openrouter", "Cond": "req_body_json_prefix_in(\"model\", \"openrouter/\", false)", "targets": [ { "ClusterName": "cluster_openrouter", "Model": "", "Weight": 100 } ], "fallbacks": [ { "ClusterName": "cluster_fallback", "Model": "" } ] }, { "name": "user_a-default", "Cond": "default_t()", "targets": [ { "ClusterName": "cluster_default", "Model": "", "Weight": 100 } ] } ] } }, "ApikeyRouteTableBindings": { "ak_user_a": ["apikey_ak_user_a"] } }

规则按顺序求值:请求 body 中model字段以openrouter/开头时命中第一条规则,路由到cluster_openrouter,失败时回退到cluster_fallback;其余请求由default_t()兜底走cluster_default。

按 Prompt/请求体大小分流的完整示例可参考 设计文档:

{ "name": "long-body-route", "Cond": "req_body_larger_than(8192)", "targets": [ {"cluster": "cluster_long_context", "model": "", "weight": 100} ], "fallbacks": [] }, { "name": "short-body-route", "Cond": "req_body_less_than(2048)", "targets": [ {"cluster": "cluster_fast_cheap", "model": "", "weight": 100} ], "fallbacks": [] }, { "name": "default", "Cond": "default_t()", "targets": [ {"cluster": "cluster_default", "model": "", "weight": 100} ], "fallbacks": [] }

长文本请求(body 大于 8KB)路由到长上下文集群,短文本请求(body 小于 2KB)路由到快速廉价集群,中间段落入默认集群。

条件原语选型建议

场景推荐原语
精确匹配少量已知模型名req_body_json_in("model", "a|b|c", true)
匹配某个供应商/命名空间下所有模型req_body_json_prefix_in("model", "openrouter/", false)
匹配多个前缀家族(如gpt-、claude-)req_body_json_prefix_in("model", "gpt-|claude-", true)
长 Prompt 路由到长上下文集群req_body_larger_than(8192)
短 Prompt 路由到低成本集群req_body_less_than(2048)
字段不存在时安全兜底default_t()

总结

BFE 的四个请求 body 条件原语覆盖了两类核心路由需求:基于 JSON 内容的语义路由(精确匹配 + 前缀匹配)与基于请求体大小的容量路由(Content-Length 阈值比较)。前者依赖gjson路径解析与请求级缓存,适合 AI 网关按模型字段分流;后者基于流式转发友好的Content-Length头,适合按 Prompt 长度选择集群。两者均遵循"Fetcher + Matcher"的求值架构,取数失败时安全返回false,可放心与其他原语组合使用。进一步的学习资料包括 body 原语参考文档、条件原语索引、核心实现 build.go 与 primitive.go,以及单元测试 primitive_test.go 和 prompt 长度路由设计文档。

  • 后端
  • 网络/通信
  • 云原生

【免费下载链接】bfe

A modern layer 7 load balancer from baidu

项目地址:https://gitcode.com/gh_mirrors/bf/bfe
点击查看免费下载
上一篇:高效获取B站高质量视频:downkyi哔哩下载姬专业应用指南
下一篇:Windows安卓子系统:为什么它能让你的PC变身安卓开发神器?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询