<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>生产化 on WYF 的技术博客</title><link>https://saflyer.cn/tags/%E7%94%9F%E4%BA%A7%E5%8C%96/</link><description>Recent content in 生产化 on WYF 的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 14 Sep 2026 10:03:15 +0800</lastBuildDate><atom:link href="https://saflyer.cn/tags/%E7%94%9F%E4%BA%A7%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><item><title>模型安全检测 11：开源版生产化改造清单</title><link>https://saflyer.cn/posts/joysafety-production-hardening/</link><pubDate>Mon, 14 Sep 2026 10:03:15 +0800</pubDate><guid>https://saflyer.cn/posts/joysafety-production-hardening/</guid><description>&lt;img src="https://saflyer.cn/posts/joysafety-production-hardening/cover.webp" alt="Featured image of post 模型安全检测 11：开源版生产化改造清单" /&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;状态&lt;/strong&gt;：✅ 已完成&#10;&lt;strong&gt;前置阅读&lt;/strong&gt;：02-Mac本地部署实操.md（部署与踩坑实录）、03-技术架构与业务模块分析.md（模块与链路全景）&#10;&lt;strong&gt;日期&lt;/strong&gt;：2026-09-14&#10;&lt;strong&gt;素材来源&lt;/strong&gt;：JoySafety 仓库源码逐一核实（compose / Dockerfile / Java 源码），引用格式 &lt;code&gt;path:line&lt;/code&gt;；与提纲素材不符处以实际代码为准（见 0.3 勘误表）&#10;&lt;strong&gt;读者定位&lt;/strong&gt;：已跑通开源版，想系统评估&amp;quot;离上生产还差什么、怎么补、补完怎么验&amp;quot;的读者&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="先建全局认识差距在哪先补哪块"&gt;&lt;a href="#%e5%85%88%e5%bb%ba%e5%85%a8%e5%b1%80%e8%ae%a4%e8%af%86%e5%b7%ae%e8%b7%9d%e5%9c%a8%e5%93%aa%e5%85%88%e8%a1%a5%e5%93%aa%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;〇、先建全局认识：差距在哪、先补哪块&#10;&lt;/h2&gt;&lt;h3 id="01-两条链路回顾所有改造都落在这两条链路上"&gt;&lt;a href="#01-%e4%b8%a4%e6%9d%a1%e9%93%be%e8%b7%af%e5%9b%9e%e9%a1%be%e6%89%80%e6%9c%89%e6%94%b9%e9%80%a0%e9%83%bd%e8%90%bd%e5%9c%a8%e8%bf%99%e4%b8%a4%e6%9d%a1%e9%93%be%e8%b7%af%e4%b8%8a" class="header-anchor"&gt;&lt;/a&gt;0.1 两条链路回顾（所有改造都落在这两条链路上）&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;配置链路&lt;/strong&gt;（配置怎么流转）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-8"&gt;&lt;a class="lnlinks" href="#hl-0-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-9"&gt;&lt;a class="lnlinks" href="#hl-0-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-10"&gt;&lt;a class="lnlinks" href="#hl-0-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-11"&gt;&lt;a class="lnlinks" href="#hl-0-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-12"&gt;&lt;a class="lnlinks" href="#hl-0-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;运维/脚本 → admin(8006) 版本化状态机 → MySQL（online 配置落库）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ api 每 60s HTTP 轮询&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; api 侧 DynamicConfigService（service/DynamicConfigService.java:65）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; POST {conf.dynamic.url}（=admin /config/defense/api/conf，&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; example/safety-api/conf/application_example.properties:5）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 按每个业务的 lastChangeTimestamp 判变更（admin 侧取 business/dag/function&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 三类记录 updateTime 的最大值：safety-admin .../business/DefenseConfigServiceImpl.java:274-291）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; volatile 内存 Map 全量替换 → 回调 listener → FunctionChainBuilderImpl 重建所有业务 DAG&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; （groovy 路由脚本重新 warmup 编译；但 OkHttp client 按 type$url 静态缓存不重建 → 02 篇坑 5 根因）&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;请求链路&lt;/strong&gt;（请求怎么流转）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-5"&gt;&lt;a class="lnlinks" href="#hl-1-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-6"&gt;&lt;a class="lnlinks" href="#hl-1-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-7"&gt;&lt;a class="lnlinks" href="#hl-1-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-8"&gt;&lt;a class="lnlinks" href="#hl-1-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-9"&gt;&lt;a class="lnlinks" href="#hl-1-9"&gt;9&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;业务方 → POST /llmsec/api/defense/v2/{accessKey}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → checkParams → rateLimited（Guava 单机限流，RateLimitServiceImpl.java:30）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → verify（HMAC-SHA1 验签；accessKey 以 test 开头跳过，DefenseApiV2Controller.java:74-75）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 按 responseMode 分派：sync 同步跑 DAG；free_taxi/http/mq 投递 Redis 队列异步跑&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → DAG 逐节点执行（while 逐跳，service/execute/FunctionExecutorImpl.java:289-333）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 节点 Function 经 OkHttp 调 skill 服务（keywords/bert/knowledge）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 每个结果带耗时 appendResult 进 ctx（core/engine/AbstractFunction.java:41-59）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → groovy router 求值返回下一跳 nodeId，返回 null 则链路结束&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → sync 直接返回；异步模式把结果 lpush 进 Redis 结果队列（TTL 300s，TaskExecutor.java:15）&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="02-差距清单总表"&gt;&lt;a href="#02-%e5%b7%ae%e8%b7%9d%e6%b8%85%e5%8d%95%e6%80%bb%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;0.2 差距清单总表&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;#&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;改造项&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;现状一句话&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;爆炸半径&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;优先级&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;①&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;数据持久化&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MySQL/Redis/Vearch 全没挂数据卷&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;down&lt;/code&gt; 一次全丢&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;P0&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;②&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;安全加固&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;admin 8006 无任何鉴权且直接映射宿主机&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;拿到 8006 ≈ 关掉整条安全防线&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;P0&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;③&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;启动依赖治理&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;sleep 30/60 硬编码排序，healthcheck 被注释且路径写错&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;冷启动靠运气&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;④&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;配置热更新&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;60s 轮询能感知变更，但 OkHttp 静态缓存使超时类变更必须重启&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;变更滞后/需重启&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;⑤&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;mq responseMode&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;实现是空 stub，mq 模式实际等价 http 模式&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;业务方按 README 用会拿不到结果&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;⑥&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;可观测性&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;埋点调用遍布代码，但 MonitorAlarmService 方法体全空&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;出事故无指标可查&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;⑦&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;水平扩展&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free_taxi 分布式锁天然支持多实例，但锁实现有两处原子性瑕疵；skill 地址写死单点&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;会话锁死/双跑&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;附&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;技术栈升级&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Spring Boot 2.0.9 / Jedis 2.5.2 / fastjson 双栈混用&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;漏洞包袱+演进成本&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;P2&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="03-素材坐标勘误提纲-vs-实际代码"&gt;&lt;a href="#03-%e7%b4%a0%e6%9d%90%e5%9d%90%e6%a0%87%e5%8b%98%e8%af%af%e6%8f%90%e7%ba%b2-vs-%e5%ae%9e%e9%99%85%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;0.3 素材坐标勘误（提纲 vs 实际代码）&#10;&lt;/h3&gt;&lt;p&gt;写作前逐一核实，提纲有三处包路径偏差，&lt;strong&gt;本文一律以实际代码为准&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;提纲写法&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;实际位置&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;service/impl/MessageQueueServiceImpl.java:32&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;service/storage/MessageQueueServiceImpl.java&lt;/code&gt;（空方法 &lt;code&gt;appendResponseData&lt;/code&gt; 恰好在 :32，行号仍对得上）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;engine/FunctionChainBuilderImpl.java&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;service/execute/FunctionChainBuilderImpl.java:37&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;engine/functions/ParallelFunction.java&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;service/execute/functions/ParallelFunction.java:41&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;其余坐标（&lt;code&gt;DynamicConfigService.java:45&lt;/code&gt; 类声明、&lt;code&gt;GroovyRouter.java:27&lt;/code&gt;、&lt;code&gt;contoller/rpc/DefenseApiServiceImpl.java:25&lt;/code&gt;）均核实无误。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="一数据持久化最紧急p0"&gt;&lt;a href="#%e4%b8%80%e6%95%b0%e6%8d%ae%e6%8c%81%e4%b9%85%e5%8c%96%e6%9c%80%e7%b4%a7%e6%80%a5p0" class="header-anchor"&gt;&lt;/a&gt;一、数据持久化（最紧急，P0）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：根目录 &lt;code&gt;docker-compose.yaml&lt;/code&gt; 三个有状态组件都没挂数据卷——&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;（:8-33）：volumes 只有 &lt;code&gt;my.cnf&lt;/code&gt; 和 3 个初始化 SQL（:19-23，挂进 &lt;code&gt;/docker-entrypoint-initdb.d/&lt;/code&gt;），&lt;code&gt;/var/lib/mysql&lt;/code&gt; 留在容器可写层。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt;（:35-48）：command 只有 &lt;code&gt;redis-server --requirepass&lt;/code&gt;（:39），无 volume、未开 AOF。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Vearch&lt;/strong&gt;（:50-66）：只挂了 &lt;code&gt;config.toml&lt;/code&gt;（:58-59）；而 &lt;code&gt;deploy/thirdparty/vearch/config.toml:7&lt;/code&gt; 声明 &lt;code&gt;data = [&amp;quot;datas/&amp;quot;,&amp;quot;datas1/&amp;quot;]&lt;/code&gt; 是&lt;strong&gt;相对路径&lt;/strong&gt;，数据落在容器内工作目录（官方镜像 WORKDIR=/vearch）。&lt;/li&gt;&#10;&lt;li&gt;对照组：&lt;code&gt;deploy/docker-compose.yml:20&lt;/code&gt; 其实给 MySQL 挂了 &lt;code&gt;./thirdparty/mysql/data:/var/lib/mysql&lt;/code&gt;——源码构建路径做了持久化，发布版反而没做（该文件里 Redis/Vearch 同样没挂）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：&lt;code&gt;docker compose down&lt;/code&gt; / 误删容器 → 策略、业务、敏感词、知识条目、知识向量&lt;strong&gt;全丢&lt;/strong&gt;（02 篇 4.1 已实测，4.2 的恢复流程本质就是&amp;quot;认命重灌&amp;quot;)。分层看爆炸半径：MySQL 与 Vearch 是硬损失（向量库重建还依赖 MySQL 知识表先保住）；Redis 里全是短 TTL 易失数据（free_taxi 任务/结果队列 300s、识别结果缓存 900s——&lt;code&gt;TaskExecutor.java:15&lt;/code&gt;、&lt;code&gt;FunctionExecutorImpl.java:247&lt;/code&gt;），丢了只是在途检测任务和缓存命中率，&lt;strong&gt;Redis 持久化是锦上添花，不是生死线&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;（compose diff 示意）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-7"&gt;&lt;a class="lnlinks" href="#hl-2-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-8"&gt;&lt;a class="lnlinks" href="#hl-2-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-9"&gt;&lt;a class="lnlinks" href="#hl-2-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-10"&gt;&lt;a class="lnlinks" href="#hl-2-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-11"&gt;&lt;a class="lnlinks" href="#hl-2-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-12"&gt;&lt;a class="lnlinks" href="#hl-2-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-13"&gt;&lt;a class="lnlinks" href="#hl-2-13"&gt;13&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-14"&gt;&lt;a class="lnlinks" href="#hl-2-14"&gt;14&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-15"&gt;&lt;a class="lnlinks" href="#hl-2-15"&gt;15&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-16"&gt;&lt;a class="lnlinks" href="#hl-2-16"&gt;16&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-17"&gt;&lt;a class="lnlinks" href="#hl-2-17"&gt;17&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-18"&gt;&lt;a class="lnlinks" href="#hl-2-18"&gt;18&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-19"&gt;&lt;a class="lnlinks" href="#hl-2-19"&gt;19&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;safety-mysql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;mysql-data:/var/lib/mysql &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：数据本体&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./deploy/thirdparty/mysql/my.cnf:/etc/my.cnf.d/my.cnf &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 原有：配置与 init SQL&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;safety-redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;redis-server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--requirepass&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;${REDIS_PASSWORD}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--appendonly&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--appendfsync&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;everysec&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增 AOF&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;redis-data:/data &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;safety-vearch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./deploy/thirdparty/vearch/config.toml:/vearch/config.toml &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 原有&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;vearch-datas:/vearch/datas &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增，对齐 config.toml:7 的相对路径&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;vearch-datas1:/vearch/datas1&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mysql-data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;redis-data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vearch-datas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vearch-datas1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;两个注意点：① MySQL 的 &lt;code&gt;/docker-entrypoint-initdb.d/&lt;/code&gt; 脚本&lt;strong&gt;只在空数据目录首次启动时执行&lt;/strong&gt;——挂上已有数据的卷后改 init SQL 不会重放；② 持久化 ≠ 备份，生产上另加定时 &lt;code&gt;mysqldump&lt;/code&gt; 与 vearch 快照策略。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：&lt;code&gt;docker compose down &amp;amp;&amp;amp; docker compose --env-file .env up -d&lt;/code&gt; 后，admin 里策略/词库原样在（02 篇 4.2 的&amp;quot;重跑附录 A/B&amp;quot;步骤应可省掉）；&lt;code&gt;docker exec safety-redis ls /data&lt;/code&gt; 能看到 &lt;code&gt;appendonlydir&lt;/code&gt;；删掉 vearch 容器重建后 &lt;code&gt;curl http://localhost:8817&lt;/code&gt; 查 space 仍在。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="二启动依赖治理p1"&gt;&lt;a href="#%e4%ba%8c%e5%90%af%e5%8a%a8%e4%be%9d%e8%b5%96%e6%b2%bb%e7%90%86p1" class="header-anchor"&gt;&lt;/a&gt;二、启动依赖治理（P1）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：根 compose 用硬编码 sleep 做启动排序——admin &lt;code&gt;sleep 30&lt;/code&gt;（:84）、api &lt;code&gt;sleep 60&lt;/code&gt;（:118）、keywords &lt;code&gt;sleep 30&lt;/code&gt;（:153）、example-init &lt;code&gt;sleep 60&lt;/code&gt;（:228）。健康检查层面更尴尬：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;MySQL/Redis/Vearch/Knowledge 有 healthcheck（:29-33/:44-48/:62-66/:193-197，其中 vearch 的 &lt;code&gt;nc&lt;/code&gt; 检查是假警报，见 02 篇坑 3）；&lt;/li&gt;&#10;&lt;li&gt;admin/api 的 healthcheck 被整段注释（:94-98/:128-132），且路径写的是 &lt;code&gt;/health&lt;/code&gt;——&lt;strong&gt;实际是 &lt;code&gt;/basic/healthcheck&lt;/code&gt;&lt;/strong&gt;（&lt;code&gt;contoller/HealthCheckController.java:14-24&lt;/code&gt;，类级 &lt;code&gt;@RequestMapping(&amp;quot;/basic&amp;quot;)&lt;/code&gt;）；该控制器故意注入 &lt;code&gt;ConfigService&lt;/code&gt;（:17-19），保证返回 ok 时配置已首载完成，语义很对；&lt;/li&gt;&#10;&lt;li&gt;admin 源码里&lt;strong&gt;根本没有&lt;/strong&gt; health 端点（全模块 grep &lt;code&gt;health&lt;/code&gt; 无匹配）；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;deploy/docker-compose.yml&lt;/code&gt; 反面教材：给 api→admin、knowledge→vearch 配了 &lt;code&gt;condition: service_healthy&lt;/code&gt;（:137-138/:83-85），但被依赖方既无 compose healthcheck、镜像 Dockerfile 里也无 &lt;code&gt;HEALTHCHECK&lt;/code&gt; 指令（safety-admin/Dockerfile、safety-api/Dockerfile 均无），compose v2 会因&amp;quot;依赖未声明 healthcheck&amp;quot;直接报错拒启动。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：sleep 是&amp;quot;按京东内网机器速度拍的数&amp;quot;——Mac 模拟 x86 场景 Java 启动更慢，60s 未必够；api 在配置首载完成前接流量就是 02 篇的 &lt;code&gt;token错误/业务不存在&lt;/code&gt; 窗口；example-init 的 sleep 60 同理，快了白等、慢了初始化脚本直接打挂。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;：① 给 admin 补一个同款 &lt;code&gt;/basic/healthcheck&lt;/code&gt; 端点（或引 actuator）；② 放开 api/admin/keywords 的 healthcheck 并把路径改成 &lt;code&gt;/basic/healthcheck&lt;/code&gt;；③ &lt;code&gt;depends_on&lt;/code&gt; 全部升级为 &lt;code&gt;condition: service_healthy&lt;/code&gt;；④ 删掉四处 sleep；⑤ example-init 把 &lt;code&gt;sleep 60&lt;/code&gt; 换成对 admin/api 健康端点的轮询循环（容器内 &lt;code&gt;curl&lt;/code&gt; 重试到 200 再执行初始化）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：&lt;code&gt;time docker compose --env-file .env up -d&lt;/code&gt; 对比冷启动总时长；&lt;code&gt;docker inspect --format '{{.State.Health.Status}}' safety-api&lt;/code&gt; 看到 healthy 后再跑 quickstart 四案例，不应再出现 token 错误窗口；故意 &lt;code&gt;docker stop safety-mysql&lt;/code&gt; 再恢复，观察依赖级联行为符合预期。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="三配置热更新完善p1"&gt;&lt;a href="#%e4%b8%89%e9%85%8d%e7%bd%ae%e7%83%ad%e6%9b%b4%e6%96%b0%e5%ae%8c%e5%96%84p1" class="header-anchor"&gt;&lt;/a&gt;三、配置热更新完善（P1）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：轮询机制本身是能自洽工作的，核心代码：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-5"&gt;&lt;a class="lnlinks" href="#hl-3-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-6"&gt;&lt;a class="lnlinks" href="#hl-3-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-7"&gt;&lt;a class="lnlinks" href="#hl-3-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// service/DynamicConfigService.java:65-77&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedRate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialDelay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :65 固定 60s 轮询&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;loadConf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConfigResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dynamicConf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loadDynamic&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :71 HTTP 拉全量 online 配置&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shouldUpdate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shouldUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preDynamicConf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dynamicConf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :72 逐业务比 lastChangeTimestamp&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;shouldUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;配置无变更&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :73-76 无变更直接返回&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;判变更依据的时间戳在 admin 侧取三方最大值（&lt;code&gt;DefenseConfigServiceImpl.java:274-291&lt;/code&gt;：business_info / execute_dag_conf / function_conf 的 &lt;code&gt;updateTime&lt;/code&gt;），所以&lt;strong&gt;改 function 也能被 api 感知&lt;/strong&gt;——感知后全量替换 volatile Map 并回调 listener 重建 DAG（&lt;code&gt;FunctionChainBuilderImpl.java:47-51&lt;/code&gt; 注册 &lt;code&gt;this::loadConf&lt;/code&gt;，:53-71 逐业务重建；Router 反射实例化在 :101-106，groovy 引擎在 &lt;code&gt;GroovyRouter.java:76-82&lt;/code&gt; → &lt;code&gt;GroovyHelper.java:18-22&lt;/code&gt; 重建并 warmup 编译）。&lt;/p&gt;&#10;&lt;p&gt;真正的坑在资源缓存：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-7"&gt;&lt;a class="lnlinks" href="#hl-4-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-8"&gt;&lt;a class="lnlinks" href="#hl-4-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-9"&gt;&lt;a class="lnlinks" href="#hl-4-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-10"&gt;&lt;a class="lnlinks" href="#hl-4-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-11"&gt;&lt;a class="lnlinks" href="#hl-4-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// service/execute/functions/FunctionResourcePool.java:20-29&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addFuncIfAbsent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RiskCheckType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;$&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// key 只有 类型+url，不含 timeout&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldFunc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 已有就直接用旧的——新 timeout 永远进不来&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;各 HTTP Function 的 OkHttp client 在 &lt;code&gt;doInit&lt;/code&gt; 时经此池 get-or-add（如 &lt;code&gt;KeywordFunction.java:58-65&lt;/code&gt;），&lt;code&gt;callTimeout&lt;/code&gt; 在首次创建时烤死。&lt;strong&gt;结论：改 url 有效（新 key 建新 client），同 url 只改 timeout 无效，必须重启 api&lt;/strong&gt;——这就是 02 篇坑 5 的代码级根因。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：① 变更最长滞后 60s，且 listener 重建走 cached 线程池异步执行（&lt;code&gt;DynamicConfigService.java:106-110&lt;/code&gt;、:195），实际生效还要再加一段不确定延迟；② 多实例各自轮询，配置切换不齐，灰度期间同业务不同实例行为可能不同；③ 单业务重建失败只记日志并保留旧 DAG（:63-68），其余业务照常替换——&lt;strong&gt;半新半旧状态只能靠人看日志发现&lt;/strong&gt;；④ 长期频繁改 url 会让旧 client 在静态池里累积（连接池+线程不释放）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;（三档，按投入选）：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;档位&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;做法&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;解决什么&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L1 小修&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;轮询周期配置化；&lt;code&gt;FunctionResourcePool&lt;/code&gt; 的 key 加 timeout（或重建前按业务 invalidate）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;重启才生效、滞后窗口&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L2 推送&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;admin 的 online/upgrade 成功后 webhook 回调 api——api 需新增一个 HTTP 端点包装已有的 &lt;code&gt;FunctionChainBuilderImpl.refresh(businessName)&lt;/code&gt;（:128-140，现无对外端点，已核实）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;秒级生效、多实例齐切&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;L3 基建&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;变更事件走 MQ 广播（正好复用第五节 mq 基建）或引入 Nacos/Apollo&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;彻底的事件驱动配置中心&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：L1 后改 function 超时&lt;strong&gt;不重启&lt;/strong&gt;，重跑 02 篇案例 3 观察耗时从 ~2000ms 截断变为正常命中；api 日志出现&amp;quot;配置更新成功&amp;quot;；L2 后从 admin upgrade 到 api 行为变化端到端 &amp;lt; 数秒，且双实例日志时间戳一致。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="四mq-responsemode-补实现p1"&gt;&lt;a href="#%e5%9b%9bmq-responsemode-%e8%a1%a5%e5%ae%9e%e7%8e%b0p1" class="header-anchor"&gt;&lt;/a&gt;四、mq responseMode 补实现（P1）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：接口实现是空 stub，全文就这么多（&lt;code&gt;service/storage/MessageQueueServiceImpl.java:30-37&lt;/code&gt;）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-3"&gt;&lt;a class="lnlinks" href="#hl-5-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-4"&gt;&lt;a class="lnlinks" href="#hl-5-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-5"&gt;&lt;a class="lnlinks" href="#hl-5-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-6"&gt;&lt;a class="lnlinks" href="#hl-5-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-7"&gt;&lt;a class="lnlinks" href="#hl-5-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-8"&gt;&lt;a class="lnlinks" href="#hl-5-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// todo 增加输出到mq&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;appendResponseData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SessionContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DefenseApiResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;appendContextData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contextData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;唯一调用点在异步任务落结果之后（&lt;code&gt;service/task/TaskExecutorImpl.java:222&lt;/code&gt;）；而请求分派处 &lt;code&gt;case mq:&lt;/code&gt; 直接落入 &lt;code&gt;case http: break;&lt;/code&gt;（&lt;code&gt;service/DefenseServiceImpl.java:171-173&lt;/code&gt;）。&lt;strong&gt;即 mq 模式今天 = http 模式 + 一次空调用&lt;/strong&gt;，结果只进 Redis 结果队列（TTL 300s）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：业务方按 README 选 mq 模式却收不到任何消息；不接 Redis 拉取接口的话结果 5 分钟后蒸发；检测数据无法进数仓做拦截率/误杀率分析（第六节也依赖它）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;（实现要点）：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;code&gt;init()&lt;/code&gt; 里建 producer（rocketmq-spring-boot 或 kafka-clients），topic 按 &lt;code&gt;safety-result-{group}&lt;/code&gt; 规划、tag=accessKey；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;appendResponseData()&lt;/code&gt; 里异步 send，message key=sessionId，body 即 response JSON；&lt;strong&gt;send 失败只记日志+计数，绝不阻塞/拖垮主检测链路&lt;/strong&gt;；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;appendContextData()&lt;/code&gt; 投递会话上下文（供多轮检测外部分析用）；&lt;/li&gt;&#10;&lt;li&gt;消费契约文档化：按 tag 过滤、按 key 归并同会话结果；&lt;/li&gt;&#10;&lt;li&gt;引依赖时留意与现有 fastjson 双栈、Spring Boot 2.0.9 的版本兼容（见第七节）。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：compose 加一个 namesrv+broker（或 kafka）单节点；发 mq 模式请求后消费端收到结构化结果；&lt;code&gt;docker stop&lt;/code&gt; broker 期间主链路审核不受影响、错误日志有兜底记录。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="五高可用与水平扩展p2"&gt;&lt;a href="#%e4%ba%94%e9%ab%98%e5%8f%af%e7%94%a8%e4%b8%8e%e6%b0%b4%e5%b9%b3%e6%89%a9%e5%b1%95p2" class="header-anchor"&gt;&lt;/a&gt;五、高可用与水平扩展（P2）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：free_taxi 的多实例互斥是设计内自带的（&lt;code&gt;service/task/TaskService.java:20-25&lt;/code&gt; 注释：&amp;ldquo;每个处理线程使用分布式锁独占会话，顺序处理&amp;rdquo;），锁实现：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-6-1"&gt;&lt;a class="lnlinks" href="#hl-6-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-2"&gt;&lt;a class="lnlinks" href="#hl-6-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-3"&gt;&lt;a class="lnlinks" href="#hl-6-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-4"&gt;&lt;a class="lnlinks" href="#hl-6-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-5"&gt;&lt;a class="lnlinks" href="#hl-6-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-6"&gt;&lt;a class="lnlinks" href="#hl-6-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-7"&gt;&lt;a class="lnlinks" href="#hl-6-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-8"&gt;&lt;a class="lnlinks" href="#hl-6-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-9"&gt;&lt;a class="lnlinks" href="#hl-6-9"&gt;9&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// service/storage/RedisStorageService.java:102-113&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expireSeconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GlobalConf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lockId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ip/线程名/时间戳（GlobalConf.java:97-99）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setnx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;returnOk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;returnOk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expireSeconds&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 第二条命令，与 setnx 非原子&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;配套机制：抢锁只试 1 次（&lt;code&gt;TaskExecutorImpl.java:73&lt;/code&gt; &lt;code&gt;TASK_WAIT_LOOP = 1&lt;/code&gt;）、抢到后每批处理前 &lt;code&gt;expire&lt;/code&gt; 续约（:135）、finally 里 &lt;code&gt;unlock&lt;/code&gt;（:156）即直接 &lt;code&gt;del&lt;/code&gt;（:116-118）。容量侧两组线程池都偏小：并行节点池 200 线程/&lt;strong&gt;队列 1&lt;/strong&gt;/CallerRunsPolicy（&lt;code&gt;service/execute/functions/ParallelFunction.java:84-93&lt;/code&gt;，:86 留着官方 todo&amp;quot;根据实际情况调整&amp;quot;），异步任务池 200/队列 10（&lt;code&gt;TaskExecutorImpl.java:56-63&lt;/code&gt;）；限流是实例级 Guava RateLimiter 内存 Map（&lt;code&gt;RateLimitServiceImpl.java:30&lt;/code&gt;）；skill 地址写死在 function 配置的 url 里（如 &lt;code&gt;KeywordFunction.java:48-50&lt;/code&gt; 校验 url 非空）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：① setnx 与 expire 两条命令之间进程崩溃 → 锁无 TTL → &lt;strong&gt;该会话永久无人处理&lt;/strong&gt;（直到人工 del）；② 锁到期被别的实例抢到后，原持有者 finally 里的 &lt;code&gt;del&lt;/code&gt; 不校验 owner，&lt;strong&gt;会误删新锁&lt;/strong&gt; → 同会话双跑、结果重复 lpush；③ 队列 1 意味着几乎无缓冲，CallerRunsPolicy 把压力回压给调用线程（DAG 主线程/Tomcat worker），同步响应被拖长——注意 Tomcat 默认 maxThreads 也是 200，三层 200 要对齐着扩容；④ N 副本实际限流 = qpsLimit × N；⑤ skill 单点。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;：锁原子化——&lt;code&gt;set(key, val, NX, PX, ttl)&lt;/code&gt; 一条命令 + Lua 脚本校验 value 后再 del（标准分布式锁写法，顺手把 jedis 升到 4.x/5.x，见第七节）；api 多副本前置 LB（nginx/网关按 URL 前缀转发即可，api 本身无状态）；skill 多副本后把 function url 换成 LB 地址或 K8s Service 名；限流换 Redis Lua 令牌桶做全局限额，或按副本数拆分配额；线程池按&amp;quot;峰值并发 × 单请求占用 function 数&amp;quot;公式估算并配置化。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：双副本 api 下 &lt;code&gt;kill -9&lt;/code&gt; 其一，另一副本应在锁 10s 过期后接管会话（日志按 lockId 对账无重复处理）；压测观察 CallerRunsPolicy 触发点即容量水位；双副本下接口限流阈值实测确认。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="六可观测性p1"&gt;&lt;a href="#%e5%85%ad%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7p1" class="header-anchor"&gt;&lt;/a&gt;六、可观测性（P1）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：最大的发现——埋点&amp;quot;管线&amp;quot;已铺遍关键路径，但汇集类是空壳：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-7-1"&gt;&lt;a class="lnlinks" href="#hl-7-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-2"&gt;&lt;a class="lnlinks" href="#hl-7-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-3"&gt;&lt;a class="lnlinks" href="#hl-7-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-4"&gt;&lt;a class="lnlinks" href="#hl-7-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-5"&gt;&lt;a class="lnlinks" href="#hl-7-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-6"&gt;&lt;a class="lnlinks" href="#hl-7-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-7"&gt;&lt;a class="lnlinks" href="#hl-7-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-8"&gt;&lt;a class="lnlinks" href="#hl-7-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// service/MonitorAlarmService.java:11-29（全文 69 行，所有方法体皆空）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MonitorAlarmService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// todo 增加监控方案 // :12&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;basicIncr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :28-29&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;parallelIncr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;biz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :34-35&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;functionIncr&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// requestIncr/apiPlanIncr/taskIncr/riskIncr 同理全空&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;调用点却一个不少：节点异常计数（&lt;code&gt;FunctionExecutorImpl.java:311&lt;/code&gt;）、并行子项失败（&lt;code&gt;ParallelFunction.java:108/134/169&lt;/code&gt;）、异步任务全流程（&lt;code&gt;TaskExecutorImpl.java:118/137/139/163/177/185&lt;/code&gt;）、DAG 构建失败（&lt;code&gt;FunctionChainBuilderImpl.java:60/67&lt;/code&gt;）。耗时埋点也已就绪：&lt;code&gt;AbstractFunction.run()&lt;/code&gt; 给每个结果 setCost 并 &lt;code&gt;appendResult&lt;/code&gt; 进 ctx（:41-59）。日志侧 log4j2 配置（&lt;code&gt;example/safety-api/conf/log4j2-example.xml&lt;/code&gt;）：RollingRandomAccessFile 100MB×10 滚动、console 只打 ERROR、&lt;code&gt;com.jd&lt;/code&gt; logger 重复声明两段（:35-40）、无 JSON 格式无 traceId。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：线上无 QPS/拦截率/错误率/function 耗时任何指标，事故定位全靠捞 &lt;code&gt;app.log&lt;/code&gt;；队列积压、锁竞争、慢 skill 全部不可见。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;：用 Micrometer（Spring Boot 2.x 自带）填 &lt;code&gt;MonitorAlarmService&lt;/code&gt; 各方法——counter 类（riskIncr/taskIncr/requestIncr）+ timer 类（包装 appendResult 处的 cost）；引 actuator 暴露 &lt;code&gt;/actuator/prometheus&lt;/code&gt;（内网或网关后，别裸奔，见第八节）；log4j2 改 JSON layout + MDC 塞 requestId/sessionId，与日志平台对接；Python skill 侧用 &lt;code&gt;prometheus_client&lt;/code&gt; 暴露推理耗时；拦截/误杀回流分析借第四节 mq 把结果入仓。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：&lt;code&gt;curl /actuator/prometheus | grep safety&lt;/code&gt; 出指标；构造一次 BERT 超时，对应 &lt;code&gt;parallel&lt;/code&gt; 错误计数 +1；Grafana 出 QPS/耗时/拦截率三块面板。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="七技术栈升级风险评估p2"&gt;&lt;a href="#%e4%b8%83%e6%8a%80%e6%9c%af%e6%a0%88%e5%8d%87%e7%ba%a7%e9%a3%8e%e9%99%a9%e8%af%84%e4%bc%b0p2" class="header-anchor"&gt;&lt;/a&gt;七、技术栈升级风险评估（P2）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：版本底账（均已核实）——&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;依赖&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;版本&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;出处&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;备注&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Spring Boot&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.0.9.RELEASE&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety-api/pom.xml:8&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2018 年末，早已 EOL 无安全补丁&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;JDK&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.8 / temurin-8&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pom.xml:20-21&lt;/code&gt;、&lt;code&gt;safety-api/Dockerfile:1&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Jedis&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2.5.2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pom.xml:54&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2015 年代，无 SetParams、无集群/ssl 现代特性&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;fastjson&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;com.alibaba:fastjson:2.0.32&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pom.xml:48&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.x 包名 API 的 2.x 兼容发行版，代码里 &lt;code&gt;com.alibaba.fastjson.*&lt;/code&gt; import 共 19 处&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;fastjson2&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;com.alibaba.fastjson2:fastjson2:2.0.46&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety-basic/pom.xml:90-93&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;DynamicConfigService&lt;/code&gt;/&lt;code&gt;RedisStorageService&lt;/code&gt; 等在用——&lt;strong&gt;两套 JSON 栈混用&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;groovy-all&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4.0.21&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pom.xml:190&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;okhttp&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4.12.0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;pom.xml:184&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;反而很新&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;并发瓶颈在 groovy 求值串行化（&lt;code&gt;core/engine/routers/GroovyRouter.java:42-46&lt;/code&gt;）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-8-1"&gt;&lt;a class="lnlinks" href="#hl-8-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-2"&gt;&lt;a class="lnlinks" href="#hl-8-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-3"&gt;&lt;a class="lnlinks" href="#hl-8-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-4"&gt;&lt;a class="lnlinks" href="#hl-8-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-5"&gt;&lt;a class="lnlinks" href="#hl-8-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;synchronized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :42 每个 router 实例一把锁&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bindings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBindings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ctx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getScript&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// :45 所有并发请求在此排队&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;parallel 归约脚本同款 synchronized（&lt;code&gt;ParallelFunction.java:155-161&lt;/code&gt;）。ScriptEngine 非线程安全才出此下策，后果是&lt;strong&gt;同一路由节点上所有请求的脚本求值串行&lt;/strong&gt;，高并发下直接顶到 p99。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：EOL 组件无补丁；fastjson 双栈序列化行为差异（&lt;code&gt;SerializerFeature&lt;/code&gt; 语义不同）埋数据类 bug；升级联动面广（Boot 3.x 要 javax→jakarta 全量替换，knife4j 4.5.0 也得换成 springdoc）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;：保守档——Boot 2.0.9→2.7.18（javax 不变，得补丁与新版 micrometer）、jedis 2.5.2→4.x（API 变动集中在 JedisPool 构造与 &lt;code&gt;set&lt;/code&gt; 参数）、fastjson 统一收敛到 fastjson2 或 Jackson；激进档——Boot 3.x + JDK 17，按模块灰度。groovy 瓶颈：脚本预编译缓存（&lt;code&gt;GroovyShell.parse&lt;/code&gt; 一次、运行期只跑 &lt;code&gt;Script.run()&lt;/code&gt; 并池化 Binding），或把热点路由逻辑下沉成 Java Router 实现（ROUTER_MAP 本就支持新增类型）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：升级后全量回归 quickstart 四案例 + admin 全接口 CRUD + free_taxi 链路；JMeter 对 groovy 路由节点压测对比升级前后 QPS/p99。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="八安全加固p0"&gt;&lt;a href="#%e5%85%ab%e5%ae%89%e5%85%a8%e5%8a%a0%e5%9b%bap0" class="header-anchor"&gt;&lt;/a&gt;八、安全加固（P0）&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;现状&lt;/strong&gt;：admin 全部管理接口零鉴权——&lt;code&gt;safety-admin&lt;/code&gt; 源码 grep &lt;code&gt;Interceptor|Filter|Authorization|auth&lt;/code&gt; 无任何匹配，pom 也无 spring-security/shiro；而根 compose 把 8006 直接映射宿主机（:87-88）。其余面：test 开头 accessKey 免签名是写死在代码里的特赦（&lt;code&gt;DefenseApiV2Controller.java:74-75&lt;/code&gt;）而非配置项；演示 secretKey=123456、&lt;code&gt;.env&lt;/code&gt; 默认口令 123456；vearch &lt;code&gt;skip_auth = true&lt;/code&gt;（config.toml:16）、&lt;code&gt;signkey = &amp;quot;secret&amp;quot;&lt;/code&gt;（:13）；RPC 控制器 &lt;code&gt;DefenseApiServiceImpl&lt;/code&gt; 整体被注释停用（:24-25 &lt;code&gt;//todo&lt;/code&gt; + &lt;code&gt;//@Service&lt;/code&gt;）——当前无 RPC 暴露面，但&lt;strong&gt;改造时切勿顺手放开注释而不加鉴权&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：拿到 8006 即可下线/篡改全部安全策略，等于从内部关掉整条防线——比任何单个绕过都危险；test 后门在生产可被直接免验签调用；弱口令 + 镜像未扫描。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;改法&lt;/strong&gt;：网络层先行（最便宜最有效）：compose 不映射 8006 到宿主机、仅内网可达，前置 nginx 加 basic auth/IP 白名单；应用层跟上：admin 加统一签名校验过滤器（可直接复用 &lt;code&gt;SignUtil&lt;/code&gt; 的 HMAC-SHA1），test 特赦改成配置开关且生产默认关；密钥轮换：business 增加&amp;quot;上一密钥&amp;quot;字段，双密钥并行验签一个窗口期再作废旧钥；容器加固：非 root 运行、只读根文件系统、镜像扫描进 CI。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：无凭证 curl 8006 各管理接口应一律 401；生产配置下 test accessKey 请求被拒；轮换演练：换钥后旧 key 在窗口内通过、窗口外 403；&lt;code&gt;docker compose config&lt;/code&gt; 确认 8006 不再暴露宿主机。&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="九改造路线图与坐标速查"&gt;&lt;a href="#%e4%b9%9d%e6%94%b9%e9%80%a0%e8%b7%af%e7%ba%bf%e5%9b%be%e4%b8%8e%e5%9d%90%e6%a0%87%e9%80%9f%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;九、改造路线图与坐标速查&#10;&lt;/h2&gt;&lt;h3 id="91-建议实施波次"&gt;&lt;a href="#91-%e5%bb%ba%e8%ae%ae%e5%ae%9e%e6%96%bd%e6%b3%a2%e6%ac%a1" class="header-anchor"&gt;&lt;/a&gt;9.1 建议实施波次&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;波次&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;内容&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;理由&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;第一波（本周）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;①数据持久化 + ②安全加固网络层&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;都是纯 compose/部署改动，不改一行代码，堵住两个 P0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;第二波（两周）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;③启动治理 + ⑥可观测性 + ⑤mq&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;建立&amp;quot;看得见、起得稳、流得出&amp;quot;的运维底座&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;第三波（排期）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;④热更新 L2/L3 + ⑦水平扩展 + 附·技术栈保守档&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;要动代码和压测，放在底座稳固之后&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="92-本文引用坐标速查"&gt;&lt;a href="#92-%e6%9c%ac%e6%96%87%e5%bc%95%e7%94%a8%e5%9d%90%e6%a0%87%e9%80%9f%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;9.2 本文引用坐标速查&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;主题&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;位置（均相对仓库根目录）&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;根 compose（无数据卷/sleep）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;docker-compose.yaml:19-23,39,58-59,84,118,153,228&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;开发版 compose 对照&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;deploy/docker-compose.yml:20,83-85,137-138&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;源码构建入口&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;deploy/build_all.sh:11-33&lt;/code&gt;、&lt;code&gt;safety/safety-api/build/build.sh:4-6&lt;/code&gt;、&lt;code&gt;safety/safety-api/Dockerfile:1-27&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;配置轮询&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/DynamicConfigService.java:65,106-110,113-151&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;变更时间戳（admin 侧）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-admin/.../service/business/DefenseConfigServiceImpl.java:274-291&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;DAG 重建&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/execute/FunctionChainBuilderImpl.java:47-71,128-140&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;OkHttp 静态缓存（坑 5 根因）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/execute/functions/FunctionResourcePool.java:20-29&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;mq 空 stub&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/storage/MessageQueueServiceImpl.java:30-37&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;mq 分派落空&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/DefenseServiceImpl.java:171-173&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;free_taxi 锁&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/storage/RedisStorageService.java:102-118&lt;/code&gt;、&lt;code&gt;service/task/TaskExecutorImpl.java:73-188&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;线程池参数&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;service/execute/functions/ParallelFunction.java:84-93&lt;/code&gt;、&lt;code&gt;service/task/TaskExecutorImpl.java:56-63&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;监控空壳&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../service/MonitorAlarmService.java:11-69&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;耗时埋点&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-basic/.../core/engine/AbstractFunction.java:41-59&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;groovy 串行求值&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-basic/.../core/engine/routers/GroovyRouter.java:42-46&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;版本底账&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/pom.xml:8,48,54,190&lt;/code&gt;、&lt;code&gt;safety/safety-basic/pom.xml:90-93&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;健康端点&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../contoller/HealthCheckController.java:14-24&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;停用的 RPC 控制器&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;safety/safety-api/.../contoller/rpc/DefenseApiServiceImpl.java:24-25&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;与前几篇的联动：02 篇坑 4（两层超时）的完整答案在 03 篇 3.4；坑 5（改配置需重启）的代码级根因在本文第三节；4.2 的灾后重灌流程在本文第一节落地数据卷后可退役。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;</description></item></channel></rss>