☰
BFE mod_wasmplugin 规则配置详解:mod_wasm.data 插件调用规则与插件元信息全解析
2026/10/10 1:48:11 网站建设 项目流程
  • 后端
  • 网络/通信
  • 云原生

【免费下载链接】bfe

A modern layer 7 load balancer from baidu

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

导读

mod_wasm.data是 BFE 开源七层负载均衡器中mod_wasmplugin模块的规则配置文件,它以 JSON 格式描述 wasm 插件在什么条件下被调用、调用哪些插件,以及每个插件实例的运行参数。本文围绕该文件逐字段展开:先讲清规则层(BeforeLocationRules / ProductRules)与插件层(PluginMap)的完整配置语义,再说明插件文件的存放布局与 md5 校验要求,最后结合bfe_modules/mod_wasmplugin/下的源码剖析配置加载、热更新与请求处理链路,帮助你完整掌握在 BFE 中落地 wasm 插件扩展的配置方法。

一、文件定位:mod_wasmplugin 的两级配置体系

mod_wasmplugin模块的配置由两个文件组成,分工明确:

文件作用
mod_wasm.conf基础配置(INI 格式),指定规则数据文件路径Basic.DataPath与插件文件目录Basic.WasmPluginPath,以及是否开启 debug 日志Log.OpenDebug
mod_wasm.data规则配置(JSON 格式),本文主角,配置 wasm 插件的调用规则及插件元信息

基础配置决定了mod_wasm.data的读取位置。仓库自带的示例 conf/mod_wasm/mod_wasm.conf 如下:

[basic] DataPath = mod_wasm/mod_wasm.data WasmPluginPath=wasm_plugin/ [log] OpenDebug=true

对应源码结构体见 bfe_modules/mod_wasmplugin/conf_mod_wasmplugin.go:Basic.WasmPluginPath缺省时默认回退为mod_wasm,Basic.DataPath缺省时默认回退为mod_wasm/mod_wasm.data,两者均会经bfe_util.ConfPathProc相对配置根目录解析为绝对路径。也就是说,如果你不额外配置,模块会从配置根目录下的mod_wasm/mod_wasm.data加载规则、从mod_wasm/目录寻找插件文件。

二、规则配置:何时调用哪些插件

mod_wasm.data的第一大类内容是规则配置,完整字段说明如下(沿用原文档配置表):

配置项类型参数含义必填补充描述合法性条件
VersionString配置文件版本Y通常采用时间戳格式,如20190101000000类型为 Version
BeforeLocationRulesArrayHandleBeforeLocation 回调点的 wasm 插件规则列表N--
BeforeLocationRules[]Object一条 wasm 插件规则Y--
BeforeLocationRules[].CondString匹配请求或连接的条件Y语法详见 Condition-
BeforeLocationRules[].PluginListArray条件匹配时执行的 wasm 插件列表Y--
BeforeLocationRules[].PluginList[]Stringwasm 插件名Y插件名须在PluginMap中已定义-
ProductRulesObject各产品线的 wasm 插件规则列表N以产品线名称为键-
ProductRules{k}String产品线名称Y--
ProductRules{v}Array产品线下的 wasm 插件规则列表Y--
ProductRules{v}[]Object一条 wasm 插件规则Y--
ProductRules{v}[].CondString匹配请求或连接的条件Y语法详见 Condition-
ProductRules{v}[].PluginListArray条件匹配时执行的 wasm 插件列表Y--
ProductRules{v}[].PluginList[]Stringwasm 插件名Y插件名须在PluginMap中已定义-

2.1 Version:热更新的版本门闩

Version以字符串标识本次规则配置的版本。从源码 bfe_modules/mod_wasmplugin/plugin_rule_load.go 可以看到,updatePluginConf的第一步就是版本比对:

if conf.Version != nil && *conf.Version != t.GetVersion() {

只有当新配置的Version与内存中PluginTable当前版本不一致时,才会真正执行插件映射重建、规则编译与旧插件清理。因此,每次修改mod_wasm.data后必须同步更新Version字段,否则即使通过热加载接口重载,新规则也不会生效。常见做法是使用时间戳(如20240101000000),该约定与 配置文件版本(Version)公共类型 一致。

2.2 BeforeLocationRules:路由前执行的插件

BeforeLocationRules挂在 BFE 的HandleBeforeLocation回调点上,此时请求尚未完成产品线/集群定位,适合执行与路由无关的通用处理(如请求头改写、审计等)。该列表按数组顺序逐条匹配:从源码 bfe_modules/mod_wasmplugin/mod_wasmplugin.go 可见,处理器遍历规则表,命中第一条Cond匹配的规则后即取出其PluginList并依次执行,随后把过滤器实例暂存到request.Context["mod_wasm_before_location_key"],供响应阶段回调。

2.3 ProductRules:按产品线差异化调度

ProductRules以产品线名称(product)为键,值为该产品线下的一组规则,其结构与BeforeLocationRules完全相同。它挂在HandleFoundProduct回调点上,此时请求已定位到具体产品线,处理器通过request.Route.Product查表(见 plugin_table.go 的Search(product)方法),实现不同产品线的插件策略隔离。例如可以为 A 产品线启用流量审计插件,为 B 产品线启用不同的改写插件,互不干扰。

两个规则列表在 plugin_rule_load.go 的buildRuleList中走同一套编译流程:先用condition.Build(*r.Cond)将条件字符串编译为可执行的条件对象(编译失败则整体加载失败),再逐个校验PluginList中的插件名是否已在PluginMap中注册,未注册会直接返回unknown plugin: xxx错误。

三、插件配置:PluginMap 定义插件元信息

mod_wasm.data的第二大类内容是插件配置PluginMap,完整字段说明如下:

配置项类型参数含义必填补充描述合法性条件
PluginMapObjectwasm 插件字典Y以插件名称为键-
PluginMap{k}Stringwasm 插件名Y--
PluginMap{v}Objectwasm 插件详细信息Y--
PluginMap{v}.NameStringwasm 插件名Y须与PluginMap{k}一致-
PluginMap{v}.WasmVersionStringwasm 插件文件版本Y用于匹配插件的 wasm 文件版本-
PluginMap{v}.ConfVersionStringwasm 插件配置文件版本Y用于匹配插件的自定义配置文件版本-
PluginMap{v}.InstanceNumIntegerwasm 插件运行实例数Y-须为非负整数

PluginMap相当于整个规则文件的插件注册表:规则中的PluginList只是按名字引用,真正的插件实体(wasm 字节码、配置、实例池)都由这里定义。对应源码结构体为 plugin_rule_load.go 中的PluginMeta,加载时会被转换为bfe_wasmplugin.WasmPluginConfig并调用bfe_wasmplugin.NewWasmPlugin构建插件实例。

3.1 Name:键值一致性约束

Name必须与PluginMap{k}(即 JSON 中的键名)一致,加载时以键名pn作为真正的插件名参与路径拼接与查表(见buildNewPluginMap中PluginName: pn的传参)。建议直接让两者同名,避免混淆。

3.2 WasmVersion 与 ConfVersion:插件热更新的双版本锚点

这两个字段分别标识wasm 二进制版本与插件自定义配置文件(PlugName.conf)版本。它们的核心价值体现在热更新时对“插件是否变化”的判断(plugin_rule_load.go):

configOld := plugOld.GetConfig() if configOld.WasmVersion == p.WasmVersion && configOld.ConfigVersion == p.ConfVersion { // not change, just copy to new map pmNew[pn] = plugOld

当新旧配置的WasmVersion与ConfVersion都未变化时,模块直接复用内存中的旧插件实例(只按需扩容实例数),避免无意义的插件重建;只要任一版本号变化,就会重新读取插件文件、校验 md5 并重建实例池。这为插件代码与配置的独立灰度发布提供了版本锚点。

3.3 InstanceNum:并发能力与扩容语义

InstanceNum表示该插件常驻的运行实例数,须为非负整数。源码 bfe_wasmplugin/plugin.go 中有一个值得注意的默认行为:

instanceNum := wasmConfig.InstanceNum if instanceNum <= 0 { instanceNum = runtime.NumCPU() }

即配置为 0 或负数时,会回退为当前机器 CPU 核数。实例池由EnsureInstanceNum动态伸缩(扩容时逐实例注册 ABI、调用ProxyOnContextCreate/ProxyOnVmStart/ProxyOnConfigure启动;缩容时Stop被裁掉的实例)。请求处理时通过GetInstance()以轮询方式从实例池中取一个可用实例,Acquire成功才占用,用完由ReleaseInstance归还——实例数越大,可同时承载的并发请求越多,但内存占用也越高,需要按业务峰值权衡。

四、wasm 插件文件布局:三件套与 md5 校验

PluginMap只声明插件“叫什么、什么版本、几个实例”,插件的实体文件需要预先就位。对于名为PlugName的插件,其文件必须存放于<WasmPluginPath>/PlugName/目录下(<WasmPluginPath>即基础配置中的Basic.WasmPluginPath):

文件名描述
PlugName.wasmwasm 文件(插件二进制字节码)
PlugName.md5PlugName.wasm 的 md5 文件(内容为该 wasm 文件的 md5 摘要)
PlugName.conf插件自定义配置文件(字节内容会通过ProxyOnConfigure传给插件)

以仓库示例 conf/mod_wasm/mod_wasm.data 中的headers插件为例,文件应放在wasm_plugin/headers/下,包含headers.wasm、headers.md5、headers.conf三个文件。

这"三件套"的加载逻辑集中在 bfe_wasmplugin/plugin.go 的loadWasmBytes:

  • 依次读取.wasm、.conf、.md5三个文件,任一缺失或为空都会返回对应的ErrWasmBytesLoad/ErrConfigFileLoad/ErrMd5FileLoad错误;
  • 读取.md5文件内容后取第一个空白分隔字段作为期望摘要,再对.wasm文件内容实际计算md5.Sum,两者不一致时返回ErrWasmBytesIncorrect("incorrect hash of wasm bytes"),拒绝加载被篡改或损坏的插件文件。

因此在更换插件二进制时,务必同步更新对应的.md5文件,否则热更新会直接失败。

五、完整配置示例:一个可直接落地的 JSON

原文档给出的完整示例(也是仓库 conf/mod_wasm/mod_wasm.data 的结构原型):

{ "Version": "20240101000000", "BeforeLocationRules": [{ "Cond": "req_path_prefix_in(\"/headers\", false)", "PluginList": [ "headers" ] }], "ProductRules": { "local_product": [{ "Cond": "default_t()", "PluginList": [] }] }, "PluginMap": { "headers": { "Name": "headers", "WasmVersion": "20240101000000", "ConfVersion": "20240101000000", "InstanceNum": 20 } } }

逐段解读:

  • BeforeLocationRules:当请求路径以/headers为前缀(不区分大小写,case_insensitive=false)时,在路由定位前执行名为headers的 wasm 插件。req_path_prefix_in是 BFE 内置条件原语,参数含义与示例详见 条件原语说明。
  • ProductRules:为产品线local_product配置了一条默认规则default_t()(恒真条件,对应 bfe_basic/condition/build.go 中的DefaultTrueCond),PluginList为空数组表示命中后不执行任何插件,等价于为产品线兜底放行。default_t()与空PluginList的组合常用于"占位规则",避免查表不到规则时报错。
  • PluginMap:注册headers插件,Name与键名一致,WasmVersion/ConfVersion均设为20240101000000,常驻20个运行实例。

其中Cond字段使用的是 BFE 的 条件表达式语法,支持req_host_in(...)、req_method_in(...)等内置原语,以及&&、||、!、括号组合,可按需写出复杂的匹配逻辑,不限于示例中的单原语形式。

六、源码视角:加载与热更新链路

理解mod_wasm.data的完整生命周期,有助于排查"改了配置不生效"类问题。核心链路如下:

  1. 模块初始化:ModuleWasm.Init加载基础配置后立即调用loadConfData(nil)完成首次规则加载,并在HandleBeforeLocation、HandleFoundProduct、HandleReadResponse三个回调点注册过滤器(见 mod_wasmplugin.go)。
  2. 文件解析:pluginConfLoad使用 BFE 的 json 解码器读取mod_wasm.data,映射为PluginConfFile结构(plugin_rule_load.go)。
  3. 插件重建:updatePluginConf先比对Version,随后buildNewPluginMap依据WasmVersion/ConfVersion决定复用还是重建插件,再buildRuleList编译条件与校验插件名,最后PluginTable.Update原子替换整张配置表(plugin_table.go 使用读写锁保护)。
  4. 旧插件清理:cleanPlugins对未变化的插件按需收缩实例数,对已删除或版本变化的插件调用OnPluginDestroy与Clear(将实例数归零)。
  5. 热更新:模块通过 web 监控框架注册了loadConfData重载处理器(web_monitor.WebHandleReload),运维可借助 BFE 的 web 监控端口对该模块触发配置重载;重载时若Version未变,整个更新会被跳过。

请求处理时,规则以"先BeforeLocationRules、后ProductRules"的顺序依次在对应回调点执行,所有命中的插件过滤器统一存入请求上下文,待HandleReadResponse阶段按逆序调用各过滤器的ResponseHandler并OnDestroy释放(见 mod_wasmplugin.go),形成完整的请求/响应双向处理闭环。

仓库测试 plugin_rule_load_test.go 使用stubWasmPlugin假插件验证了规则加载、版本变化、插件复用等路径,无需真实 wasm 运行时即可覆盖配置逻辑,可作为理解字段行为的参考。

七、配置自检清单

结合文档与源码,落地mod_wasm.data时建议依次确认:

  1. Version每次变更后递增或改为新时间戳,否则热更新不会触发;
  2. PluginList中引用的每个插件名都在PluginMap中注册,且Name与键名一致;
  3. PluginMap中每个插件都在<WasmPluginPath>/<插件名>/下备齐.wasm、.md5、.conf三件套,且.md5与.wasm内容严格匹配;
  4. InstanceNum按并发预期设置,0 或负数会被回退为 CPU 核数;
  5. Cond语法符合 条件表达式规范,可在加载日志中确认无编译错误;
  6. 基础配置 mod_wasm.conf 中DataPath、WasmPluginPath指向正确且目录可读。

完成以上检查后,即可通过 BFE 的配置热加载机制安全地发布、灰度或回退 wasm 插件能力。

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

【免费下载链接】bfe

A modern layer 7 load balancer from baidu

项目地址:https://gitcode.com/gh_mirrors/bf/bfe
点击查看免费下载
上一篇:react-error-boundary源码中的hasArrayChanged函数解析
下一篇:使用 Unity BDD 宏编写 Given/When/Then 风格的 C 语言行为驱动测试

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

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

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

立即咨询