RuleMatcher 规则匹配
同一个插件,能对不同路由/域名/服务用不同配置——这靠 _rules_ 机制。今天拆 rule_matcher.go:配置怎么写、怎么被拆成一条条带匹配条件的规则。这是 wasm-go 最有特色的能力。
_rules_ 就是这本分区规章:顶层是"默认规矩"(全局配置),_rules_ 里每条是"某区专属规矩"(匹配条件 + 该区配置)。这跟"公司报销制度=公司默认规则+部门可覆盖"一模一样。今天先看规章怎么被拆成一条条规则,明天(D08)看请求来了怎么对号入座。为什么要规则匹配
RuleMatcher 就干这个:把"按位置区分配置"的能力做进 SDK,所有插件白拿。_rules_ 配置,由 wasm-go 的 RuleMatcher 在数据面解析匹配。控制面写、数据面读的闭环。_rules_ 配置格式
常量在 rule_matcher.go:46-52:_rules_、_match_route_、_match_domain_、_match_service_、_match_route_prefix_。配置长这样:
# 顶层键(除 _rules_)= 全局配置
block_urls: ["/global"]
# _rules_ = 规则数组
_rules_:
- _match_route_: ["route-a"] # 匹配路由 route-a
block_urls: ["/a-only"]
- _match_domain_: ["*.example.com"] # 匹配域名
block_urls: ["/example-only"]
_rules_ 之外的键构成"全局配置",_rules_ 里每一项是一条规则(含匹配条件 + 该规则专属配置)。这套约定由 wasm-go 定义,控制面(Higress/Console)按它生成配置。globalConfig,_rules_ 里每项变成一条带 category 和匹配集合的 RuleConfig。明天请求来了就在这几条里挑一条。五种 Category
rule_matcher.go:30-37:
const (
Route Category = iota // 按路由名匹配(_match_route_)
Host // 按域名匹配(_match_domain_)
Service // 按服务名匹配(_match_service_)
RoutePrefix // 按路由名前缀匹配(_match_route_prefix_)
RouteAndService // 路由 + 服务都匹配
)
_match_xxx_ 自动推断(L06)。RuleConfig 结构
rule_matcher.go:57-64:
type RuleConfig[PluginConfig any] struct {
category Category
routes map[string]struct{} // 匹配的路由名集合
services map[string]struct{}
routePrefixs map[string]struct{}
hosts []HostMatcher // 域名匹配(带类型)
config PluginConfig // 这条规则专属的配置
}
map[string]struct{} 存路由/服务集合——这是 Go 里"集合"的惯用法(struct{} 不占内存,只用 key 判存在)。查一个路由名在不在,map 的 O(1) 比遍历 slice 快。RuleMatcher(:116-120)持有 []RuleConfig + globalConfig + hasGlobalConfig。解析四类匹配
ParseRuleConfig(:182+)对每条规则调四个 parse 方法(:307+):
rule.routes = m.parseRouteMatchConfig(ruleJson) // 读 _match_route_
rule.hosts = m.parseHostMatchConfig(ruleJson) // 读 _match_domain_
rule.services = m.parseServiceMatchConfig(ruleJson) // 读 _match_service_
rule.routePrefixs = m.parseRoutePrefixMatchConfig(ruleJson) // 读 _match_route_prefix_
例如 parseRouteMatchConfig(:307-317)把 _match_route_ 数组读进 map[string]struct{}。
category 推断
rule_matcher.go:233-247:解析完四类匹配后,按"有没有"推断 category:
if boolToInt(hasRoute)+boolToInt(hasService)+boolToInt(hasHosts)+boolToInt(hasRoutePrefix) == 0 {
return errors.New("...at least one of _match_route_/_match_domain_/_match_service_/_match_route_prefix_...")
}
if hasRoute {
rule.category = Route
if hasService { rule.category = RouteAndService } // 路由+服务
} else if hasHosts {
rule.category = Host
} else if hasService {
rule.category = Service
} else {
rule.category = RoutePrefix
}
_match_,category 自动定。| 规则写了哪些 _match_ | hasRoute/Service/Hosts/Prefix | 推断结果 |
|---|---|---|
| _match_route_:[a] | ✓ / ✗ / ✗ / ✗ | Route |
| _match_route_:[a] + _match_service_:[s] | ✓ / ✓ / ✗ / ✗ | RouteAndService |
| _match_domain_:[*.ex.com] | ✗ / ✗ / ✓ / ✗ | Host |
| _match_service_:[s] | ✗ / ✓ / ✗ / ✗ | Service |
| (一个 _match_ 都没写) | ✗ / ✗ / ✗ / ✗ | ❌ 报错:至少写一种 |
Host 匹配三型
域名匹配比路由复杂(要支持通配)。MatchType(:39-44):Prefix(*.example.com)/ Exact(www.example.com)/ Suffix(example.*)。HostMatcher(:54-57)存 matchType + host。
*.example.com(前缀通配,匹配所有子域名)、example.*(后缀通配)、精确域名。SDK 解析域名时判断通配符位置,归类成 Prefix/Suffix/Exact,运行时(明天)用对应逻辑匹配。这比路由名(只精确或前缀)更细。GenerateHashKey(:67-113)还能为规则生成稳定哈希键(用于规则级隔离的备份定位,Day 09)。👶 小白:*.example.com 里的 *,能匹配 a.b.example.com 这种多级子域名吗?
👨🏫 老师:这类通配的具体匹配逻辑在明天(D08)的运行时 hostMatch 里。今天你只要记住:SDK 解析域名时先看 * 在开头还是结尾,把规则归成 Prefix / Suffix / Exact 三种"型号",运行时再按型号套对应的比较逻辑。今天是"给规则贴标签",明天才是"拿请求去比对"。
今日小结 + 动手
🧠 今天你应该能回答
- 为什么需要
_rules_?和 Console 的四作用域什么关系? - 五种 Category 分别按什么匹配?
- 为什么用
map[string]struct{}存路由集合? - category 怎么从
_match_xxx_推断?域名为什么要三种匹配型?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/wasm-go
sed -n '30,64p' pkg/matcher/rule_matcher.go
sed -n '225,247p' pkg/matcher/rule_matcher.go
sed -n '307,330p' pkg/matcher/rule_matcher.go
:authority/route_name/cluster_name 逐条规则匹配,选出该用哪份配置。