全局配置 vs 规则配置
昨天匹配出"用哪份配置"。今天看两份配置怎么关联:规则配置能"继承"全局配置再覆盖部分字段。还有一个生产级特性——规则级隔离:一条规则配错不拖垮其他规则。
两种解析方式
ParseRuleConfig(rule_matcher.go:182)接受两个解析函数:
parsePluginConfig:独立解析一份配置(全局或规则各自解析)。parseOverrideConfig:解析规则配置时能拿到全局配置做基础(继承+覆盖)。
ParseOverrideConfig
plugin_wrapper.go:218-223:
func ParseOverrideConfig[PluginConfig any](
f ParseConfigFunc[PluginConfig], // 解析全局配置
g ParseRuleConfigFunc[PluginConfig], // 解析规则配置(带 global 参数)
) CtxOption[PluginConfig] {
return &parseOverrideConfigOption[PluginConfig]{parseConfigF: f, parseRuleConfigF: g}
}
// 规则解析函数签名(:65):
// func(json gjson.Result, global PluginConfig, config *PluginConfig) error
// ↑ 全局配置作为基础传进来
g 多了个 global PluginConfig 参数——这就是"继承"的入口。你在 g 里通常先 *config = global(拷贝全局作为默认),再按规则 JSON 覆盖需要改的字段。继承 + 覆盖(典型写法)
func parseGlobalConfig(json gjson.Result, config *MyConfig) error {
config.timeout = json.Get("timeout").Int() // 全局默认 timeout
config.retries = json.Get("retries").Int()
return nil
}
func parseRuleConfig(json gjson.Result, global MyConfig, config *MyConfig) error {
*config = global // 1. 先继承全局
if json.Get("timeout").Exists() {
config.timeout = json.Get("timeout").Int() // 2. 只覆盖本规则写了的
}
return nil
}
*config = global(继承全部默认),再按规则 JSON 覆盖写了的字段——没写的字段自动保留全局值。{"timeout":5,"retries":3} + 规则A {"_match_route_":["a"],"timeout":10} → 规则A 最终配置 = {timeout:10, retries:3}(timeout 被覆盖、retries 继承)。若规则B 只写
{"_match_route_":["b"]} 不改任何字段 → 规则B 最终配置 = {timeout:5, retries:3}(完全等于全局)。全局配置先解析
rule_matcher.go:200-215:ParseRuleConfig 先解析全局(_rules_ 外的顶层键),存进 m.globalConfig + 置 hasGlobalConfig=true,再解析每条规则(此时能把 globalConfig 传给 override 函数)。
globalConfigError,但只要有成功的规则仍可继续(:210-214)。空配置(keyCount==0)则直接把整份配置当全局(:191-194),这对应"整个插件全局启用"的最简情况。规则级隔离
iface/context.go:32-35 的 EnableRuleLevelConfigIsolation 开关,配合 ParseRuleConfig(rule_matcher.go:255-292)的容错逻辑:
:280 直接 return err)——太脆弱。开启规则级隔离后:一条规则解析失败,只跳过这条,其他规则照常生效(:257-278)。生产环境里,运维改错了某一条路由的插件配置,不至于让所有路由的这个插件都挂掉。这是把"故障爆炸半径"缩到最小的稳健设计。👶 小白:既然隔离模式这么稳,为什么不默认开、非要插件自己 EnableRuleLevelConfigIsolation()?
👨🏫 老师:因为"跳过坏规则继续跑"不总是好事。有些插件(比如强安全策略)宁可"配错就整体不生效、逼你立刻修",也不能"带着一条坏规则半生效"——那可能造成安全口子。所以 SDK 把选择权交给插件:要稳健就开隔离,要严格就不开。"按场景选合适的容错强度",比一刀切更成熟。
从备份恢复
规则级隔离还有第二层保护(rule_matcher.go:258-273):一条规则解析失败时,尝试 loadRuleJsonFromBackup 加载上次成功的配置;成功解析的规则会 storeRuleToBackup 存进备份(:284-291)。备份用规则的 GenerateHashKey(Day 07)定位。
容错策略对比
rule_matcher.go:298-305 收尾的两种模式对比:
// 隔离模式:全失败且无备份 → 报错;否则用成功的(含备份恢复的)
if isRuleLevelIsolation && len(successfulRules)==0 && hasAnyRuleParseError {
return fmt.Errorf("all rules failed to parse and no previous successful config available")
// 非隔离模式:只要有一条失败就整体失败(前面已 return);这里是"一条都没解析出"
} else if !isRuleLevelIsolation && len(successfulRules)==0 {
return fmt.Errorf("no valid rules parsed")
}
EnableRuleLevelConfigIsolation() 自己选,SDK 两种都支持。今日小结 + 动手
🧠 今天你应该能回答
- ParseConfig 和 ParseOverrideConfig 的区别?
- 规则配置怎么"继承+覆盖"全局配置?为什么好?
- 规则级隔离解决什么问题?默认为什么脆弱?
- "从备份恢复"怎么做?备份存哪?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/wasm-go
sed -n '218,230p' pkg/wrapper/plugin_wrapper.go
sed -n '182,305p' pkg/matcher/rule_matcher.go
grep -n "loadRuleJsonFromBackup\|storeRuleToBackup" pkg/matcher/rule_matcher.go