<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>插件系统 on 止语Lab</title>
        <link>https://www.wujiachen.com.cn/tags/%E6%8F%92%E4%BB%B6%E7%B3%BB%E7%BB%9F/</link>
        <description>Recent content in 插件系统 on 止语Lab</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Mon, 20 Apr 2026 22:33:54 +0800</lastBuildDate><atom:link href="https://www.wujiachen.com.cn/tags/%E6%8F%92%E4%BB%B6%E7%B3%BB%E7%BB%9F/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>别用 Go 写插件系统——但如果你非要写，这里有张决策表</title>
            <link>https://www.wujiachen.com.cn/posts/go-plugin-system/</link>
            <pubDate>Mon, 20 Apr 2026 22:33:53 +0800</pubDate>
            <guid>https://www.wujiachen.com.cn/posts/go-plugin-system/</guid>
            <description>&lt;img src=&#34;https://www.wujiachen.com.cn/&#34; alt=&#34;Featured image of post 别用 Go 写插件系统——但如果你非要写，这里有张决策表&#34; /&gt;&lt;p&gt;&#xA;&lt;img src=&#34;https://img.wujiachen.com.cn/go-plugin-system/cover.png&#34; alt=&#34;封面&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Go 不适合写插件系统，这不是偏见，是设计事实。&lt;/p&gt;&#xA;&lt;p&gt;plugin 包从 Go 1.8 引入，到今天九年了，Go 团队的态度很明确：文档的 Warnings 章节罗列了 8 项重大缺陷——平台限制、竞态检测支持差、部署复杂、初始化推理难、安全风险、版本强耦合、依赖必须一致、建议用 IPC 替代。这不是&amp;quot;还在打磨&amp;quot;，是&amp;quot;我们明确警告你别用&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;为什么？因为 Go 的静态编译模型和运行时动态加载在根本上互斥。&lt;/p&gt;&#xA;&lt;h2 id=&#34;一go-不适合插件不是偏见&#34;&gt;&lt;a href=&#34;#%e4%b8%80go-%e4%b8%8d%e9%80%82%e5%90%88%e6%8f%92%e4%bb%b6%e4%b8%8d%e6%98%af%e5%81%8f%e8%a7%81&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;一、&amp;ldquo;Go 不适合插件&amp;quot;不是偏见&#xA;&lt;/h2&gt;&lt;p&gt;Go 官方对 plugin 包的态度已经说明一切：文档没有&amp;quot;逐步完善&amp;quot;的路线图，只有一个越来越长的警告列表。我翻了一下 Go 1.8 到 1.26 的 commit 历史，plugin 包的核心 API（&lt;code&gt;Open&lt;/code&gt;、&lt;code&gt;Lookup&lt;/code&gt;）在九年里没有任何变更，连签名都没动过。一个真正在推进的实验性功能不会这样。&lt;/p&gt;&#xA;&lt;p&gt;我实测了 plugin 包的核心行为（完整代码见 &lt;a class=&#34;link&#34; href=&#34;https://github.com/wujiachen0727/zhiyulab-evidence/tree/main/go-plugin-system&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;zhiyulab-evidence/go-plugin-system&lt;/a&gt;）：编译两个版本的 plugin（.so 共享库文件），用 &lt;code&gt;plugin.Open&lt;/code&gt; 加载 v1，再对同一个路径调用 &lt;code&gt;plugin.Open&lt;/code&gt;——两次返回的是同一个 &lt;code&gt;*Plugin&lt;/code&gt; 指针（&lt;code&gt;p1 == p2: true&lt;/code&gt;）。Go runtime 按文件路径缓存已加载的 plugin，同路径不会重新加载。你编译新版本到不同路径（v2.so）？可以加载，但旧版本无法卸载——Go 没有 &lt;code&gt;plugin.Close&lt;/code&gt;，内存只增不减。热更新？做不到。&lt;/p&gt;&#xA;&lt;p&gt;还有平台限制：plugin 只支持 Linux、FreeBSD 和 macOS，Windows 不支持，远非全平台。如果你的服务跑在 Windows 上——不少企业内网工具确实是 Windows 部署——plugin 包直接出局。&lt;/p&gt;&#xA;&lt;p&gt;版本耦合是最容易被低估的坑。plugin 和主程序必须在完全相同的 Go 版本下编译，go.mod 中所有共享依赖的版本也必须一致。这意味着你的主程序升了 Go 1.27，所有 plugin 必须同时重编。在微服务环境下，你升级主服务的 Go 版本时需要同时协调所有插件方的重新编译和发布，把&amp;quot;热插拔&amp;quot;变成了&amp;quot;冷重启&amp;rdquo;。plugin 卖的是&amp;quot;独立扩展&amp;quot;，实际交付的是版本强绑定。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://img.wujiachen.com.cn/go-plugin-system/ch1-compile-model-conflict.png&#34; alt=&#34;编译模型 vs 插件化冲突&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;说白了就一个原因：Go 压根就不想让你运行时加载代码。它的默认路径是静态编译、单一二进制，动态加载不在核心设计路径上。plugin 包是在这条路上硬开的一个口子。&lt;/p&gt;&#xA;&lt;p&gt;问题不是要不要扩展，是怎么扩展。&lt;/p&gt;&#xA;&lt;h2 id=&#34;二5-种方案的真实代价&#34;&gt;&lt;a href=&#34;#%e4%ba%8c5-%e7%a7%8d%e6%96%b9%e6%a1%88%e7%9a%84%e7%9c%9f%e5%ae%9e%e4%bb%a3%e4%bb%b7&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;二、5 种方案的真实代价&#xA;&lt;/h2&gt;&lt;p&gt;我跑了一组实测 + 交叉验证了社区数据，结果可能和你想的不一样。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;测试方法&lt;/strong&gt;：每种方案执行相同的加法运算（&lt;code&gt;Add(1, 2)&lt;/code&gt;），用 Go benchmark 框架自适应迭代，测量稳态调用延迟（不含首次 Open/Lookup 的一次性成本）。环境是 Go 1.26.2 / darwin/arm64 / Apple M3 Max。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;方案&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;延迟 (ns/op)&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: right&#34;&gt;相对原生&lt;/th&gt;&#xA;          &lt;th&gt;代价标签&lt;/th&gt;&#xA;          &lt;th&gt;数据来源&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;原生函数调用&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~1&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;1x&lt;/td&gt;&#xA;          &lt;td&gt;无（基准线）&lt;/td&gt;&#xA;          &lt;td&gt;[实测]&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;plugin 接口调用&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~1-3&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~1x&lt;/td&gt;&#xA;          &lt;td&gt;不可卸载 + 版本强耦合&lt;/td&gt;&#xA;          &lt;td&gt;[实测]&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;yaegi 解释器&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~370&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~370x&lt;/td&gt;&#xA;          &lt;td&gt;兼容性坑 + 解释器性能&lt;/td&gt;&#xA;          &lt;td&gt;[实测]&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;wazero WASM&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~30&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~30x&lt;/td&gt;&#xA;          &lt;td&gt;API 约束 + 生态早期&lt;/td&gt;&#xA;          &lt;td&gt;[实测]（编译模式）&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;go-plugin (gRPC)&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~30000-50000&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: right&#34;&gt;~30000x&lt;/td&gt;&#xA;          &lt;td&gt;每次调用 30-50μs&lt;/td&gt;&#xA;          &lt;td&gt;[引用: go-plugin-benchmark]&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://img.wujiachen.com.cn/go-plugin-system/ch2-five-solutions-race.png&#34; alt=&#34;5 种方案横评对比&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;最快的方案最危险。&lt;/strong&gt; 因为 plugin 加载后在同一进程地址空间内执行，本质是 &lt;code&gt;dlopen&lt;/code&gt; + 符号查找，加载后就是函数指针调用。但代价是致命的：不能卸载、不能热更新、版本必须与主程序完全匹配（包括 Go 版本和 go.mod 中所有共享依赖的版本）。你的主程序升个 Go 版本，所有 plugin 都得重编。&lt;/p&gt;&#xA;&lt;p&gt;go-plugin 是另一个极端。HashiCorp 出品的 RPC 插件框架，把插件跑在独立进程里，通过 gRPC 通信。崩溃隔离、版本独立、跨平台，三个都做到了。原理不复杂：主进程通过 &lt;code&gt;os/exec&lt;/code&gt; 启动插件子进程，双方通过 stdin/stdout 上的 protobuf 编码消息通信（低延迟场景用 gRPC，高吞吐场景甚至可以用 raw TCP）。插件崩了？主进程检测到退出码，自动重启。插件用的 Go 版本和主进程不一样？无所谓，进程间只有协议约束，没有符号约束。&lt;/p&gt;&#xA;&lt;p&gt;代价是每次调用 30-50μs。听起来不多？算一笔账：如果你的请求链路上每个环节都要调插件，一秒 1000 次，那就是 30-50ms 花在通信上。对于 API 网关这种单请求延迟敏感的场景，这可能是不可接受的。但 Terraform 的 Provider（每个云厂商的 API 适配层）就是这么干的——因为 Provider 调用频率低（一次 &lt;code&gt;terraform apply&lt;/code&gt; 调用几十次），而且安全隔离的价值远大于延迟代价。&lt;/p&gt;&#xA;&lt;p&gt;yaegi 和 wazero 各有甜蜜点。yaegi（Traefik 团队维护的 Go 解释器）直接在进程内解释执行 Go 代码，~370ns/op，比 RPC 快 100 倍但比原生慢 370 倍。不过 yaegi 不完全支持 &lt;code&gt;reflect&lt;/code&gt; 和 &lt;code&gt;unsafe&lt;/code&gt;，依赖这些包的插件编译通过但运行时可能 panic；goroutine/channel 的支持也有边界案例。举个例子：如果你的插件用 &lt;code&gt;reflect.TypeOf()&lt;/code&gt; 做动态类型判断，在 yaegi 里可能拿不到预期的结果——因为解释器内部的数据布局和编译后的不同。这类坑不会在编译期暴露，只在运行时炸——Traefik 自己的 yaegi 使用文档里专门列了一页兼容性清单，哪些能用哪些不能用，清单本身就能说明问题。&lt;/p&gt;&#xA;&lt;p&gt;wazero（纯 Go 实现的 WASM 运行时，无 CGO 依赖）~30ns/op（编译模式），比 yaegi 快 10 倍+，而且有沙箱隔离——插件代码运行在 WASM 虚拟机里，无法访问宿主的文件系统、网络或内存。代价是 WASM 的 API 约束——插件不能随便用 Go 标准库，只能通过导出/导入函数和共享线性内存与宿主通信。想用 &lt;code&gt;net/http&lt;/code&gt; 发个请求？不行，得让宿主代劳，插件通过函数调用把请求参数传出来。也就是说，你从&amp;quot;什么都能用&amp;quot;切换到了&amp;quot;只能用宿主允许的&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;以上延迟是纯调用开销。真实场景中插件本身业务逻辑耗时会远大于调用开销——比如插件执行 1ms 业务逻辑时，yaegi 的 370ns 只占 0.037%。选方案看调用开销占总耗时的比例，不是绝对值。&lt;/p&gt;&#xA;&lt;p&gt;代价清楚了，但&amp;quot;代价&amp;quot;要结合场景看才有意义。&lt;/p&gt;&#xA;&lt;h2 id=&#34;三不用插件是多数场景的最优解&#34;&gt;&lt;a href=&#34;#%e4%b8%89%e4%b8%8d%e7%94%a8%e6%8f%92%e4%bb%b6%e6%98%af%e5%a4%9a%e6%95%b0%e5%9c%ba%e6%99%af%e7%9a%84%e6%9c%80%e4%bc%98%e8%a7%a3&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;三、&amp;ldquo;不用插件&amp;quot;是多数场景的最优解&#xA;&lt;/h2&gt;&lt;p&gt;这是本文最反直觉的结论：多数以为需要插件系统的场景，其实不需要。&lt;/p&gt;&#xA;&lt;h3 id=&#34;场景-1配置热更新&#34;&gt;&lt;a href=&#34;#%e5%9c%ba%e6%99%af-1%e9%85%8d%e7%bd%ae%e7%83%ad%e6%9b%b4%e6%96%b0&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;场景 1：配置热更新&#xA;&lt;/h3&gt;&lt;p&gt;你的 API 网关需要动态调整限流阈值。第一反应：&amp;ldquo;加个插件系统让配置能热更新&amp;rdquo;。但仔细想想，你需要的不是新代码，是新数据。&lt;/p&gt;&#xA;&lt;p&gt;但配置变更是&lt;strong&gt;数据变更&lt;/strong&gt;，不是逻辑变更。数据变更不需要加载新代码，你需要的是配置中心（etcd/consul/nacos）+ 内存热加载。etcd 的 watch 通知延迟通常 &amp;lt; 10ms，比任何插件方案的加载时间都快。&lt;/p&gt;&#xA;&lt;p&gt;具体怎么干？一个 goroutine watch etcd 的 key 变化，收到通知后原子替换内存中的限流配置对象——用 &lt;code&gt;sync.AtomicValue&lt;/code&gt; 或读写锁都行。整个过程不涉及编译、不涉及动态加载、不涉及进程间通信。比 plugin 方案简单一个数量级，比 go-plugin 方案快三个数量级。&lt;/p&gt;&#xA;&lt;p&gt;配置热更新不需要插件系统，配置中心就是正解，别把简单问题搞复杂了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;场景-2规则引擎&#34;&gt;&lt;a href=&#34;#%e5%9c%ba%e6%99%af-2%e8%a7%84%e5%88%99%e5%bc%95%e6%93%8e&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;场景 2：规则引擎&#xA;&lt;/h3&gt;&lt;p&gt;表达式引擎天然适合规则场景。govaluate 处理简单算术/逻辑表达式，~50ns/op（社区数据）；cel-go（Google 的 Common Expression Language）支持类型检查、宏、自定义函数，~200ns/op，Kubernetes 用它做 admission policy。&lt;/p&gt;&#xA;&lt;p&gt;规则是&lt;strong&gt;受限逻辑&lt;/strong&gt;——不是任意代码，是结构化的条件判断。受限逻辑不需要插件，需要的是表达式引擎。简单规则用 govaluate/cel-go 足矣，复杂规则（涉及外部调用、状态聚合）可能需要 yaegi 或 wazero 级别的方案，但那已经是&amp;quot;受限的代码执行&amp;quot;而非&amp;quot;规则引擎&amp;quot;了——边界要划清。&lt;/p&gt;&#xA;&lt;p&gt;为什么表达式引擎比插件更适合规则场景？四个字：&lt;strong&gt;可控可审&lt;/strong&gt;。表达式是声明式的，可以序列化、版本管理、A/B 测试、回滚。插件是命令式的，你不知道它 &lt;code&gt;init()&lt;/code&gt; 里干了什么，也不容易审计它的副作用。&lt;/p&gt;&#xA;&lt;h3 id=&#34;场景-3第三方扩展&#34;&gt;&lt;a href=&#34;#%e5%9c%ba%e6%99%af-3%e7%ac%ac%e4%b8%89%e6%96%b9%e6%89%a9%e5%b1%95&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;场景 3：第三方扩展&#xA;&lt;/h3&gt;&lt;p&gt;&#xA;&lt;img src=&#34;https://img.wujiachen.com.cn/go-plugin-system/ch3-no-plugin-coffee.png&#34; alt=&#34;不用插件喝咖啡&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;DevOps 平台需要让第三方开发者写自定义集成。这才是真正需要插件化的场景——你管不了它的代码（不可信 + 质量不可控），它还需要独立部署和版本独立。&lt;/p&gt;&#xA;&lt;p&gt;这种场景下，原生 plugin 直接出局：无沙箱、崩溃不隔离。一个第三方 plugin 里的 nil pointer dereference 会直接把你主进程干掉。yaegi 在同一进程内运行，panic 会传播到宿主，且无内存/文件系统隔离，隔离不够彻底。剩下两个选择：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;go-plugin (RPC)&lt;/strong&gt;：进程隔离 + 崩溃隔离 + 版本独立。被 Terraform 大规模验证。代价是 30-50μs/call，但第三方扩展通常是低频调用（每秒几次），这个延迟完全可接受。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;wazero (WASM)&lt;/strong&gt;：沙箱隔离 + ~30ns/op + 无 CGO 依赖。新兴方案，API 约束较多但方向正确。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;第三方扩展是唯一真正需要插件化的场景。go-plugin 是当前最成熟选择，wazero 是未来方向。&lt;/p&gt;&#xA;&lt;p&gt;还有一类场景：内部多团队模块化（Caddy 中间件、Telegraf input/output plugin 模式）。这类场景需要灵活组装但不需要沙箱隔离——都是自己人写的代码，崩溃了直接找作者。Caddy 的做法是编译时注册中间件，不是运行时加载：写一个实现 &lt;code&gt;caddy.Module&lt;/code&gt; 接口的 Go 文件，import 进来，&lt;code&gt;caddy&lt;/code&gt; 命令重新编译。Telegraf 也类似，所有 input/output plugin 都是编译时内嵌的。这种&amp;quot;插件化&amp;quot;的本质是模块化——代码独立维护、统一编译部署，跟运行时动态加载是两回事。&lt;/p&gt;&#xA;&lt;h2 id=&#34;四决策表你的场景选什么&#34;&gt;&lt;a href=&#34;#%e5%9b%9b%e5%86%b3%e7%ad%96%e8%a1%a8%e4%bd%a0%e7%9a%84%e5%9c%ba%e6%99%af%e9%80%89%e4%bb%80%e4%b9%88&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;四、决策表——你的场景选什么&#xA;&lt;/h2&gt;&lt;p&gt;上面拆了 5 种方案的代价，拆了场景的需求。现在把它们拼在一起。&lt;/p&gt;&#xA;&lt;h3 id=&#34;决策口诀&#34;&gt;&lt;a href=&#34;#%e5%86%b3%e7%ad%96%e5%8f%a3%e8%af%80&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;决策口诀&#xA;&lt;/h3&gt;&lt;p&gt;你要问自己两个问题，第一个最重要：&lt;strong&gt;代码是不是你自己的？调用频率有多高？&lt;/strong&gt; 如果代码是你自己的且频率不低，那不需要插件。&lt;/p&gt;&#xA;&lt;p&gt;展开来说：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;该不该？&lt;/strong&gt; 数据变更不需要插件，逻辑变更才需要。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;信不信？&lt;/strong&gt; 不可信代码必须隔离，可信代码不需要插件化。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;快不快？&lt;/strong&gt; 高频场景 RPC 不行，低频随便选。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://img.wujiachen.com.cn/go-plugin-system/ch4-decision-magnifier.png&#34; alt=&#34;决策口诀&#34; loading=&#34;lazy&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;方案推荐&#34;&gt;&lt;a href=&#34;#%e6%96%b9%e6%a1%88%e6%8e%a8%e8%8d%90&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;方案推荐&#xA;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;多数人该选的：不用插件。&lt;/strong&gt; 配置变更用配置中心，规则用表达式引擎，模块化用编译时注册。多数场景到这步就结束了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;真正需要插件时：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;go-plugin (RPC)&lt;/strong&gt; → 第三方扩展、安全要求高、调用频率低&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;wazero (WASM)&lt;/strong&gt; → 需要沙箱 + 高性能的实验性场景&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;yaegi&lt;/strong&gt; → 进程内动态执行受限 Go 代码（先验证兼容性）&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;不推荐：plugin 包。&lt;/strong&gt; 除非你只在 Linux/FreeBSD/macOS 上跑且能接受所有限制。&lt;/p&gt;&#xA;&lt;p&gt;Go 不适合动态加载，这是设计选择不是缺点。插件化的需求是真实的，但满足需求的方式不止动态加载一种。如果你在评估 Go 插件方案，先用决策口诀过滤场景，然后在方案推荐里找到你的行，跑一个最小 POC 验证性能和兼容性。别硬上，换个角度。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;附录实验代码和原始数据&#34;&gt;&lt;a href=&#34;#%e9%99%84%e5%bd%95%e5%ae%9e%e9%aa%8c%e4%bb%a3%e7%a0%81%e5%92%8c%e5%8e%9f%e5%a7%8b%e6%95%b0%e6%8d%ae&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;附录：实验代码和原始数据&#xA;&lt;/h2&gt;&lt;p&gt;本文 2 组实验的代码和原始结果已开源：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;GitHub：&lt;a class=&#34;link&#34; href=&#34;https://github.com/wujiachen0727/zhiyulab-evidence/tree/main/go-plugin-system&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;zhiyulab-evidence/go-plugin-system&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;plugin-benchmark/&lt;/code&gt; — 5 方案稳态调用延迟基准测试（Go benchmark 框架，自适应迭代）&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;plugin-reload/&lt;/code&gt; — plugin.Open 同路径缓存 + 不同路径新版本加载实验&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;scenarios/&lt;/code&gt; — 3 个典型场景的推理记录&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;data/&lt;/code&gt; — RPC 开销影响推演数据&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;每个子目录都有独立 README，说明如何复现。二进制编译产物不入库，跑实验前自己 &lt;code&gt;go build&lt;/code&gt;。&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
