第 4 卷

图里的第 4 步“层叠”,就是三者真实的装配先后,dsh 把各层依次叠到空条目列表上:
- 各 bundle 自带的 patch,按 dsh.profile.bundles 的顺序,dsh-base 最先
- profile 的 cordis.patch.yml
- home 级 patch
- 每个 --patch overlay
后层按 id 覆盖前层,并且是整行替换,不是增量合并,覆盖时要重述所有想保留的字段,只写改动的那一个会让其余字段消失。
这三样在实际里怎么用,本讲主要关注两种:开发调试用 patch overlay 往跑着的宿主里塞能力,生产部署把插件打包成组合包装进 profile。
开发调试:dsh 已经在跑,怎么往里加东西,又怎么验证加对了。这一讲我们备了 firewall-plugin.ts,这个插件只挂引擎的两个 Provider,还配了一份 patch。它不需要
配置,所以条目里没写 config 字段,要给插件传配置时才写:
3- id: firewall# 新增条目的 id,后面的 patch 按它定位这一行 4name: "./firewall-plugin.ts" # 从哪加载:本地文件写相对路径
复制代码 1dsh --profile demo --patch ./src/firewall/ch18/cordis.patch.yml --dump-config
--dump-config 只打印组合后的配置树,不真正启动,因此不需要 key,跑一下就能看到 overlay 那段叠在最后,按 id 命中同一条 firewall,把 name 从包名换成本地文件。
生产部署:打包成组合包装进 profile,启动即上线。装的就是前面那个组合包 packages/dsh-firewall-rule/,装到本机不需要发布,add 直接指向本地目录即可:
复制代码 1# ① 构建(在 cookbook 根;子包没有自己的 node_modules,prepare 跑不了)
2npm run build:ch18-bundle 3
4# ② 发布(只发给别人时需要;本机自用跳过)
5npm pack# 打包成 tgz;或 npm publish 发到 registry 6
7# ③ 装进 profile 8dsh plugin --profile demo add -w ./packages/dsh-firewall-rule
9 10# ④ 启动
11dsh --profile demo
第 ③ 步只是把包追加进上文 profile 里的 dsh.profile.bundles 列表。add 的参数就是 “从哪装”:本机自用写本地目录,即 -w;② 打包或发布后,换成 tgz 路径,比如 add
./dsh-firewall-rule-0.1.0.tgz,或 npm 包名,比如 add dsh-firewall-rule。
规则语言:一条规则 = 条件树(when 命中 + unless 例外)+ 动作(deny 优先、与顺序无关)。
引擎判定:编译一次、求值多次;匹配前先清洗请求;只走 RE2 子集,测试替身不加载就拒绝;超大字段在入口先拒,不进引擎。
插件化:引擎借 dsh 的能力 seam 切成 Definition / Provider / Consumer 三角色,再用插件装配挂上树。
组装与部署:profile 决定装哪些 bundle、按什么顺序,bundle 与 patch 叠成插件树;开发调试用 overlay 塞能力,生产部署打包装进 profile、启动生效。
这背后真正的变化是判定从“写死的 if”变成数据:规则是 JSON、能增删、每条结果可解释。
扩展从改宿主变成加插件:引擎只不过是插件,装进 dsh 只靠声明、不改宿主源码。
下一讲我们看规则怎么可生成、可修改、可持久化。
注:本讲 GitHub 链接
思考题
动手实操:示例在 src/firewall/ch18/,直接改代码、重跑验证。
- 把 rules.json 里 block admin scan 的 unless 删掉,重跑 npm run
start:ch18,/admin/health 从放行变成拦截。再想:若改成“在 deny 规则后面再加一条放行规则、靠顺序盖住它”,行得通吗?为什么例外必须写进规则本身,即 unless,
而不能靠排队?
- 把 re2-matcher.ts 里 SafeRe2Matcher.compile 换成用 RegExp 实现,重跑,引
擎起不来了。为什么一个能正常匹配的 RegExp,反而让引擎拒绝启动?
欢迎把你的运行结果分享出来。如果你觉得这节课对你有帮助,也欢迎分享给其他朋友,我们