kgateway 策略合并框架全解析:从 `MergePolicies` 泛型算法到多层级策略继承

发布时间:2026/10/12 3:39:58

kgateway 策略合并框架全解析:从 `MergePolicies` 泛型算法到多层级策略继承 API网关云原生微服务【免费下载链接】kgatewayThe Cloud-Native API Gateway and AI Gateway项目地址https://gitcode.com/gh_mirrors/kg/kgateway点击查看免费下载kgateway 是一款云原生 API 网关与 AI 网关。当多个策略Policy同时附着到同一路由、监听器或后端时kgateway 通过一套通用的策略合并框架把多条策略折叠为一条有效策略再交由插件执行翻译。本文以仓库中的 devel/policy_merging/overview.md 为骨架结合 pkg/pluginsdk/policy/merge.go 等源码完整讲解合并框架的触发时机、两种合并维度、四种合并策略、优先级排序、合并来源追踪MergeOrigins以及Settings.PolicyMerge的深度合并配置。读完本文你将掌握 kgateway 策略合并的设计思想并具备为新增策略插件实现合并逻辑的完整能力。什么是 kgateway 中的策略合并策略合并是**插件自愿参与plugin opt-in**的机制某个策略插件是否参与合并取决于它在 PolicyPlugin 中是否设置了MergePolicies字段。未设置MergePolicies时的行为如果插件没有提供MergePolicieskgateway 会按策略顺序逐条应用每个被附着的策略每ApplyForRoute/ApplyForListener一次。这本身并不会产生一个通用的胜者选择规则——kgateway 仍然保持下文所述的策略顺序并按该顺序对每条附着策略各调用一次插件但最终结果由插件自己的 apply 逻辑决定如果插件只是反复写入同一个输出字段那么后应用者获胜后来者覆盖先前的写入如果插件实现了自己的增量合并或优先级感知的 apply 逻辑则由插件特有逻辑决定谁赢。内置的 route policy 路径就是第二种情况的典型例子它没有实现PolicyPlugin.MergePolicies但在ApplyForRoute期间仍然使用了合并风格逻辑从而保证低优先级的继承策略不会自动覆盖高优先级的值。这一点在 pkg/pluginsdk/ir/iface.go 的接口注释中有直接说明当SupportsPolicyMerge返回 false 时ApplyForRoute对每条策略调用一次否则只对MergePolicies的返回结果调用一次。设置MergePolicies时的行为如果插件提供了MergePolicieskgateway 会先调用合并函数得到唯一一份合并结果再对这份结果应用一次。也就是说合并发生在插件 apply 之前插件面对的是一个已经合并好的 IR。当前使用者与非使用者今天pkg/pluginsdk/policy/merge.go 中的通用框架被以下插件使用TrafficPolicy流量策略trafficpolicy/merge.goListenerPolicy监听器策略listenerpolicy/merge.go值得注意的非使用者内置 route policy自己做增量合并 / apply 逻辑不走通用框架BackendTLSPolicy不使用通用框架而是只挑选一个获胜者winner takes all。通用合并框架在什么时候运行框架的触发条件是翻译过程中存在多条同一GroupKind的附着策略且该插件已选择加入MergePolicies。当前主要场景有两类同一有效附着层级上的多条策略多条同类策略附着在同一个 HTTPRoute rule 或 route 上多条同类策略附着在同一个 listener / HCM 作用域上多条同类策略附着在同一个 backend 上。HTTPRoute 委托delegation继承子 route 从委托链中的父 route 继承策略。几个重要的当前细节Listener 翻译目前把listener 附着和gateway 附着的 HTTP 策略当作同一个层级合并Route 翻译目前把rule 级 extension refs、rule 级附着策略和route 级附着策略当作同一个层级合并目前唯一会为通用策略合并分配不同层级的地方是被委托的父 route 链。因此在实践中同一个层级same hierarchy指的是相同的HierarchicalPriority值而不一定意味着相同的 Kubernetes 对象种类。层级信息记录在 pkg/pluginsdk/ir/gw.go 的PolicyAtt.HierarchicalPriority中。合并之前的策略排序在合并之前附着策略会先被排序。排序规则定义在 pkg/krtcollections/policy.go 的slices.SortStableFunc中kgateway.dev/policy-weight注解值大者优先同一 GroupKind 内比较权重越高优先级越高权重相同时创建时间戳creation timestamp更早者优先权重和创建时间都相同时按策略身份键policyAttSortKey即PolicyRef.IDWithSectionName()做最终确定性 tiebreaker。第三点非常关键Kubernetes 的creationTimestamp只有秒级精度多条策略如果通过一次kubectl apply同时创建很可能在权重和时间上打平如果没有最终的确定性 tiebreaker顺序将取决于上游 map 迭代Go 的 map 迭代顺序是随机的导致跨控制平面重启的合并结果不确定。因此代码用策略身份作最终兜底。排序完成后列表中靠前的策略在合并时被视作更高优先级。kgateway.dev/policy-weight注解在 api/annotations/policy.go 中定义支持负整数默认权重为 0对于基于 GatewayExtensions 实现的策略如 extAuth、extProc则使用 GatewayExtension 上指定的权重。通用合并算法MergePolicies[T any]合并的核心是 pkg/pluginsdk/policy/merge.go 中的MergePolicies[T any]()函数它做**两趟two passes**合并第一趟层级内合并same hierarchy调用groupPoliciesByHierarchicalPriority(policies)把策略按HierarchicalPriority分组按优先级从高到低slices.Backward(slices.Sorted(maps.Keys(...)))因为代码中更高的数值表示更高优先级依次处理每组每一组内部通过merge(..., true, ...)按优先级顺序折叠合并这一趟会传入插件的mergeSettingsJSON。第二趟跨层级合并across hierarchies把第一趟产生的各层级合并结果从最高层级到最低层级逐层折叠通过merge(..., false, ...)完成这一趟总是传入空的 settings 字符串注释明确说明mergeSettings does not apply when merging across hierarchies。merge内部实现核心的merge[T any]()函数merge.go有几个值得注意的实现细节累加器以空 IR 起步out的PolicyIr是零值polMergeOrigins是空 map。因此即使只有一条策略合并也会发生merge 状态和 origins 也总是会被填充这正是envoy config dump 中永远能看到 merge 元数据的原因带错误的策略会被跳过如果某条策略Errors非空错误会被包装进out.Errors并记录 Warn 日志后continue该策略不参与合并但错误被保留逐条折叠每条策略折叠时按GetMergeStrategy(policies[i].InheritedPolicyPriority, sameHierarchy)计算本次合并策略同层级合并时保留继承优先级if sameHierarchy { out.InheritedPolicyPriority policies[i].InheritedPolicyPriority }合并后的PolicyAtt不设置PolicyRef注释Merged policy should not set PolicyRef。四种合并策略通用框架在内部定义了四种合并策略见 merge.go 的常量及注释策略含义AugmentedShallowMerge保留p1中已有的值仅用p2中p1未设置nil/空的字段做填充OverridableShallowMerge让p2替换p1中已设置的字段AugmentedDeepMerge深合并但冲突时优先p1OverridableDeepMerge深合并但冲突时优先p2其中p1是当前的合并累加器accumulatorp2是即将被折叠进来的下一条策略。配套的辅助函数还有IsMergeable(p1, p2, opts)merge.go判断p2是否可以并入p1。在AugmentedShallowMerge下要求p1为 nil 且p2非 nil即只填充未设置的字段其他三种策略下只要p2非 nil 即可IsSettable(p1, opts)借助IsMergeable(p1, not-nil, opts)判断p1是否可以被设置isNil用反射判断 nil 指针、空 slice/map、空字符串等未设置状态ToInternalMergeStrategy(s)merge.go把用户可读的DeepMerge/ShallowMerge字符串映射到内部策略任何其他值都会记错误日志并回退到 shallow merge。同层级合并默认固定AugmentedShallowMerge对于sameHierarchytrueGetMergeStrategy(...)始终返回AugmentedShallowMerge。这意味着同层级合并默认是浅合并同层级合并不会查询kgateway.dev/inherited-policy-priority注解如果插件想要不同的同层级行为必须在自己的合并函数内部自行覆盖。TrafficPolicy就是这种覆盖的现行例子例如mergeHttpACLtrafficpolicy/merge.go在没有PolicyMerge设置覆盖时对同层级 ACL 默认使用AugmentedDeepMerge只有检测到冲突时才回退浅合并。跨层级合并策略由inherited-policy-priority注解决定对于sameHierarchyfalse合并策略来自拥有被继承策略的 route 上的kgateway.dev/inherited-policy-priority注解注解常量见 api/annotations/policy.go合法值见同文件的InheritedPolicyPriorityValue注解值内部策略ShallowMergePreferParentOverridableShallowMergeShallowMergePreferChildAugmentedShallowMergeDeepMergePreferParentOverridableDeepMergeDeepMergePreferChildAugmentedDeepMerge如果注解缺失或无效默认值为ShallowMergePreferChild见 merge.godefault分支返回AugmentedShallowMerge即浅合并、优先子路由。在当前代码中这主要影响被委托的 HTTPRoute 父 - 子继承。读者可以参考 devel/policy_merging/overview.md 关于路线委托背景的说明并阅读 design/10943-route-delegation.md 了解委托机制但当前行为以代码为准。合并状态与合并来源MergeOrigins合并框架不只是产出一个 IR 对象它还会追踪每个合并字段来自哪条源策略。ir.MergeOriginspkg/pluginsdk/ir/merge.go本质上是map[string]sets.Set[string]把策略 IR 字段名映射到贡献了该字段的策略引用集合。其 ID 由AttachedPolicyRef.ID()生成group/kind/namespace/name拼接见 pkg/pluginsdk/ir/gw.go。两个关键操作SetOne(...)浅替换语义把字段的 origin 置为唯一策略。若传入非 nil 的policyRef直接设为该 ref若为 nil则克隆源mergeOrigins中该字段的值见 ir/merge.goAppend(...)深合并语义把策略加入字段的 origin 集合并集表示该字段由多条策略共同构成见 ir/merge.go。这份数据驱动两件事策略附着状态报告Attached、Merged、Overridden三种状态。Envoy 过滤器元数据以merge.groupKind为 key 写入生成的 Envoy 对象。状态推导逻辑在 pkg/kgateway/translator/irtranslator/policy.go 的reportPolicyAttachmentStatus中mergeOrigins.IsSet()为 false → 非合并策略直接报告Attached否则用mergeOrigins.GetRefCount(policy.PolicyRef)见 ir/merge.go 的MergeOriginsRefCountNone/Partial/All枚举判定None该策略在所有字段的 origin 中都没出现→OverriddenPartial出现在部分字段→MergedAll出现在全部字段→Attached。元数据写入在 policy.go 的addMergeOriginsToFilterMetadata中通过mergeOrigins.ToProtoStruct()把 origins 转成 proto structkey 前缀为merge.mergeMetadataKeyPrefix。重要提醒如果某个合并函数没有正确维护MergeOrigins策略状态报告就会失真例如被覆盖的策略仍显示为Attached。另外由于合并总是从空PolicyIr开始、把已有策略合并进去即使只有一条策略merge 状态和 origins 也总是被填充——这就是为什么你在 envoy config dump 中总是能看到 merge 元数据。Settings.PolicyMergeTrafficPolicy 的深度合并开关api/settings/settings.go 以原始 JSON 字符串形式暴露Settings.PolicyMerge默认值{}。接线路径目前它只接入了TrafficPolicy插件链路为controller settings → plugin registry →trafficpolicy.NewPlugin(..., globalSettings.PolicyMerge, ...)。具体实现在 pkg/kgateway/extensions2/registry/registry.go注册时把globalSettings.PolicyMerge传给NewPlugintraffic_policy_plugin.go并在 traffic_policy_plugin.go 中把MergePolicies绑定为policy.MergePolicies(pols, mergeTrafficPolicies, mergeSettings)ListenerPolicy忽略它listenerpolicy.NewPlugin(ctx, commoncol)不接收 merge settings跨层级合并忽略它因为通用框架只在同层级这一趟传入 settings跨层级那趟传空字符串见 merge.go。支持的 key 与取值当前TrafficPolicy支持的 key 为trafficPolicy.extAuthtrafficPolicy.extProctrafficPolicy.transformationtrafficPolicy.acl对应的 JSON 结构体定义在 trafficpolicy/merge.go 的mergeOpts/TrafficPolicyMergeOpts中。被识别的覆盖值为DeepMerge和ShallowMerge任何其他值都会记录错误日志并回退到浅合并由policy.ToInternalMergeStrategy保证。配置示例{ trafficPolicy: { extProc: DeepMerge, extAuth: DeepMerge } }当TrafficPolicy合并函数检测到设置 JSON 中的非空值后会用policy.ToInternalMergeStrategy(...)替换opts.Strategy例如mergeExtProc在 merge.go 中的做法从而把同层级的默认浅合并切换为深合并。深度合并的底层原理为什么必须复制后写入在 trafficpolicy/merge.go 的注释中有一段非常重要的实现警告值得单独说明p1是每次翻译新建的累加器但它的子 IR 指针不是新的浅合并会把p1的字段直接设为p2的子 IRdefaultMerge的行为而p2的 IR 是KRT collection 的输出会被所有引用该策略的翻译共享合并流程是同层级浅合并 跨层级深合并所以任何深合并分支如果就地修改p1的子 IR就会把合并结果写回源策略本身该策略随后会把已经合并过的值贡献给下一次翻译于是配置会在不同 route 之间、不同翻译周期之间不断累积配置污染。为此代码提供了两个安全助手copyForMergeT any返回in的浅拷贝拷贝仍共享 slice/map/指针因此调用方必须整体替换字段而非穿透修改copyWithFilterConfig(...)针对 dynamic module 子 IRtransformation、httpACL用proto.Clone复制并携带新的FilterConfig。defaultMergemerge.go是通用字段合并的模板先IsMergeable检查然后按策略浅合并Set整字段 SetOneorigin或深合并仅在p1未设置时填充最后mergeOrigins.SetOne(fieldName, p2Ref, p2MergeOrigins)记录来源。如何为一个新的策略插件实现合并以下是作者为新增策略插件整理的实现路线与 overview.md 的步骤一一对应并补充源码佐证1. 在插件层面选择加入合并设置PolicyPlugin.MergePolicies并调用共享辅助函数MergePolicies: func(pols []ir.PolicyAtt) ir.PolicyAtt { return policy.MergePolicies(pols, mergeMyPolicies, /* or merge settings JSON */) },参考TrafficPolicy的写法traffic_policy_plugin.go与ListenerPolicy的写法listener_policy_plugin.go。2. 编写针对你的 IR 类型的合并函数合并函数收到的参数对应MergePolicies[T any]的mergeFn签名见 merge.go累加器p1下一条策略p2源策略引用p2Ref源策略的 merge originsp2MergeOrigins合并选项 / 策略opts policy.MergeOptions输出的 merge origins mapmergeOrigins插件 settings JSON 字符串mergeSettingsJSON函数只应修改累加器p1把p2当作只读输入。ListenerPolicy的 merge.go 展示了按字段拆分多个 mergeFunc 逐一调用的组织方式TrafficPolicy则把约 26 个字段合并函数放进mergeFuncs切片循环执行trafficpolicy/merge.go。3. 尊重当前生效的策略对于简单字段先用policy.IsMergeable(...)判断然后按浅合并语义赋值或执行插件特有的深合并。如果你的插件需要同层级覆盖参考TrafficPolicy的做法读取 settings JSON在相关字段合并函数内替换opts.Strategy例如mergeExtProc、mergeExtAuth、mergeRustformation、mergeHttpACL对tpOpts的处理。4. 边合并边记录 origins当某个字段由单条策略独占时用mergeOrigins.SetOne(...)当某个字段由多条策略共同构成时用mergeOrigins.Append(...)。5. 测试两个合并维度至少为以下场景添加测试同一层级的多条策略合并如果策略可能参与委托则测试跨委托父/子 route 层级的继承合并merge-origin 敏感行为当状态或元数据依赖 origins 时。仓库中现成的参考测试非常多例如pkg/pluginsdk/ir/merge_test.goSetOne/Append/GetRefCount等基础原语测试pkg/kgateway/extensions2/plugins/trafficpolicy/merge_test.goTrafficPolicy 各字段合并测试翻译层的 golden 文件如 traffic-policy/extauth-deep-merge.yaml、traffic-policy/acl-default-deep-merge.yaml、delegation/traffic_policy_inheritance.yaml、listener-policy-http/merge.yaml 等可直接观察合并后的 Envoy 输出形态。当前的思维模型mental model当你要新增一个可合并策略时把下面这些当作相互独立的决策该插件到底要不要合并还是让一条策略直接胜出参考对比TrafficPolicy/ListenerPolicy选择合并BackendTLSPolicy选择单赢家。两条策略指向同一有效附着点时应该发生什么同层级默认浅合并AugmentedShallowMerge除非插件自行覆盖。父策略被子 route 继承时应该发生什么由kgateway.dev/inherited-policy-priority注解决定默认ShallowMergePreferChild。对于深合并字段origins 与优先级应该如何上报SetOne独占还是Append共同构成直接决定状态是Attached/Merged/Overridden。通用框架为你提供的是层级感知的编排hierarchy-aware orchestration排序、同层级合并、跨层级合并、origin 追踪都由它完成而字段级的语义每个字段怎么合并、冲突怎么处理始终由插件自己负责。结语kgateway 的策略合并框架把多条策略折叠为一条这一横切逻辑从各插件中抽离出来统一到 pkg/pluginsdk/policy/merge.go 的泛型算法中先按HierarchicalPriority分组做同层级浅合并再按继承优先级注解跨层级折叠同时用MergeOrigins完整记录每个字段的来源驱动Attached/Merged/Overridden状态上报与 Envoymerge.groupKind元数据。对于开发者而言接入这个框架只需要选择加入 → 写字段合并函数 → 尊重策略 → 记录 origins → 覆盖两个维度的测试这五步而字段级的合并语义永远是插件自己的事。如果你要新增策略插件建议先通读 devel/policy_merging/overview.md 与本文再以TrafficPolicy、ListenerPolicy的 merge 实现为模板并参考 devel/policy_merging/acl-policy-merging.md 中 ACL 合并的具体策略说明即可快速上手。赞分享API网关云原生微服务【免费下载链接】kgatewayThe Cloud-Native API Gateway and AI Gateway项目地址https://gitcode.com/gh_mirrors/kg/kgateway点击查看免费下载相关推荐AI Scientist-v2并行代理系统深度解析如何同时探索多个研究路径AI Scientist v2并行代理系统深度解析如何同时探索多个研究路径 AI Scientist v2是一个革命性的自动化科学研究框架其核心创新在于 并人工智能AI Agent自主智能体科研Agent 工作流kgateway 路由委托Route Delegation完整指南HTTPRoute 拆分、路径继承与策略合并kgateway 路由委托Route Delegation完整指南HTTPRoute 拆分、路径继承与策略合并 kgateway 的路由委托RouteAPI网关云原生微服务FreeShow打破语言壁垒让演示文稿与世界对话FreeShow打破语言壁垒让演示文稿与世界对话 想象一下你正在为一场国际会议准备演示文稿观众来自世界各地——有说英语的、有讲中文的、有用法语的、还有用桌面应用上一篇ShawzinBotMIDI变ShawzinWarframe里3步弹完整首曲子的完整指南下一篇ShawzinBot 完全指南4 步把 MIDI 文件变成 Warframe 里 Shawzin 的演奏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/12 3:39:58

基于Python+Django的多功能校园网站开发实战:从需求到部署全流程

做校园网站这一类偏业务型的 Web 项目,最怕的不是功能多,而是模块之间没有规划好,写到后面数据表乱成一团,视图里全是重复代码。最近正好完整整理了一个基于 Python Django 的多功能校园网站项目,覆盖了新闻公告、课程…

2026/10/12 3:39:58

云手机核心原理与工程实践:从系统定制到低延迟串流落地方案

做过云手机项目的人,应该都体会过那种憋着一肚子草泥马的时刻:用户对着屏幕上那台“安卓手机”戳了半天,骂你“卡得像幻灯片”,但你本地测一切正常——因为问题出在云端那台真实设备上,CPU调度、GPU渲染、视频编码、网…

2026/10/12 3:39:58

2026届安全方向毕设选题指南:图像/网络/机器学习全解析

2026届信息工程专业的同学,现在启动毕设选题一点不早。尤其在“信息系统安全”这个大方向下,每年都有大量学生拿着标题来找我聊,开口就是“我想做安全方向的”,但具体做什么、怎么做、做到什么程度能毕业,往往一问三不…

2026/10/12 4:55:02

【Linux系统】06 进程概念

目录 ​编辑 1 冯・诺依曼体系结构 2 操作系统 (OS) 定位 2.1 广义与狭义操作系统 2.2 OS 两大目标 2.3 系统调用 & 库函数 3 进程基础概念 & PCB (task_struct) 3.1 什么是进程 3.2 PCB task_struct(Linux 的进程控制块) 3.3 查看进程…

2026/10/12 4:55:02

年终奖不发之后:绩效目标、系数规则与激励修复策略

一进十二月,办公室的气温就跟着年终奖的消息一起浮动。今年我们公司的情况很直接:官方通知就一句话——“鉴于今年公司销量、利润率等指标未达成年终目标,所以今年没有年终激励奖”。没有展开解释,没有缓冲余地,消息一…

2026/10/12 4:55:02

【Linux系统】05 Linux开发工具(下)

目录 1 make 与 Makefile 自动化构建 1.1 为什么需要 Makefile 1.2 Makefile 基础规则 1.3 make 工具推演执行逻辑 1.4 伪目标 .PHONY 1.5 Makefile 进阶语法 自定义变量 三大自动变量(高频面试) wildcard 通配符 后缀替换 模式规则 %.o:%.c …

2026/10/12 4:50:01

page_alloc zone_statistics

zone_statistics() 是页面分配路径上用于更新 NUMA 命中/未命中统计的辅助函数。它追踪分配请求的“首选 zone”与实际分配到的 zone 之间的关系,为 /proc/vmstat 提供 numa_hit、numa_miss、numa_foreign 等计数。核心作用它的职责是:当一次分配发生在 …

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑