- 后端
【免费下载链接】zoraxy
A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
Zoraxy 插件本质上就是一个带 HTTP Server / Listener 的普通 Go 程序,因此它的构建流程与编译一个普通 Go 程序完全一致:先go mod tidy整理依赖,再go build产出平台相关二进制,最后用-introspect标志校验产物是否符合 Zoraxy 的插件加载协议。本文以 6. Compile a Plugin.md 为骨架,结合仓库中的zoraxy_plugin库源码与 example 插件工程,完整讲解从目录结构、依赖引入、构建命令、内省验证到部署安装与热重载的全过程,读完即可动手编译并验证自己的第一个 Zoraxy 插件。

一、编译前的准备:插件就是"会说话"的 Go 程序
官方文档给出了最核心的定性:插件基本上就是一个带有 HTTP Server / Listener 的 Go 程序,构建插件的步骤与构建普通 Go 程序完全一样。这意味着你不需要掌握任何特殊工具链、插件 SDK 编译器或专有构建系统,只要本机具备标准的 Go 工具链(go命令)即可。
需要额外准备的是zoraxy_plugin库。该库是 Zoraxy 与插件之间通信协议的 Go 实现,同时被 Zoraxy 主程序和插件共同使用。它并非通过go get从公共仓库拉取,而是以源码目录形式直接复制进你的插件工程,例如仓库中每个示例插件的mod/zoraxy_plugin/目录:
example/plugins/helloworld/ ├── mod/ │ └── zoraxy_plugin/ # 从 src/mod/plugins/zoraxy_plugin 复制而来 │ ├── zoraxy_plugin.go # -introspect 与 -configure 的解析实现 │ ├── embed_webserver.go # 基于 embed.FS 的插件 UI 路由 │ ├── dev_webserver.go # 基于文件系统的开发模式 UI 路由 │ ├── static_router.go # 静态捕获路径路由 │ ├── dynamic_router.go # 动态捕获 sniff/ingress 路由 │ └── events/ # 事件订阅相关实现 ├── www/index.html ├── go.mod └── main.goexample/plugins/helloworld/mod/zoraxy_plugin/README.txt对此做了说明:复制整个zoraxy_plugin模块到插件的 mod 目录,保持目录结构与文件组织,即可获得处理-introspect、-configure启动流程以及内嵌 Web UI 路由的全部能力。由于该库向后兼容(其文件头注释明确 "Usually this file are backward compatible"),旧版本插件复制的旧库也能被新版 Zoraxy 正常加载。
示例插件的go.mod非常简单,只声明 module 名与 Go 版本,依赖的zoraxy_plugin库通过目录内的包路径(如example.com/zoraxy/helloworld/mod/zoraxy_plugin)直接解析,因此只要mod/zoraxy_plugin目录存在,离线也能完成go mod tidy与go build:
module example.com/zoraxy/helloworld go 1.23.6注意:如果插件引入了
mod/zoraxy_plugin之外的第三方依赖(如仓库中 upnp 示例插件 就依赖 miniupnpc),go mod tidy时会需要联网拉取依赖,并生成go.sum。
二、标准构建流程:go mod tidy+go build
原文档给出的构建命令只有三行,但在实际工程中建议配合前置检查与产物命名一起执行:
# 假设你当前位于插件工程的根目录(包含 main.go 与 go.mod) go mod tidy go build # 用 -introspect 标志验证插件是否正确构建 ./{{your_plugin_name}} -introspect # 此时插件的元信息会以 JSON 字符串打印到 STDOUT1.go mod tidy
整理插件工程的依赖。对于只依赖mod/zoraxy_plugin库的插件,该命令不会产生任何额外下载;对于有第三方依赖的插件,它会解析并写入go.sum。
2.go build
在当前目录产出二进制。go build的默认输出名取自插件根目录名(filepath.Base),这与 Zoraxy 插件管理器的自动重建逻辑一致:在 hotrebuild.go 的RebuildPlugin中,Zoraxy 使用go build -o <目录名>(Windows 下追加.exe)来产出与插件目录同名的二进制:
// src/mod/plugins/hotrebuild.go outputBinary := filepath.Base(plugin.RootDir) if runtime.GOOS == "windows" { outputBinary += ".exe" } cmd = exec.Command(goPath, "build", "-o", outputBinary, ".")3. 跨平台交叉编译与分发命名
Zoraxy 插件以平台相关二进制分发,命名约定为操作系统_CPU架构_插件名,例如linux_amd64_foobar、windows_amd64_foobar.exe、linux_arm64_foobar(见 1. What is Zoraxy Plugin.md)。编译分发版本时,可用 Go 标准的交叉编译参数:
GOOS=linux GOARCH=amd64 go build -o linux_amd64_foobar GOOS=windows GOARCH=amd64 go build -o windows_amd64_foobar.exe GOOS=linux GOARCH=arm64 go build -o linux_arm64_foobar这一设计正是插件架构选择"普通 Go 程序 + HTTP 通信"而非 Unix Socket / gRPC 的原因:跨平台、跨 CPU 架构编译几乎没有额外成本,也不存在协议转换的复杂性问题(见 1. Plugin Architecture.md)。
三、-introspect验证:编译产物能否被 Zoraxy 识别
构建完成后,必须用-introspect标志运行产物。这是 Zoraxy 插件三步流程(Introspect → Configure → Forwarding)中的第一步,也是编译验证环节的核心。
1. 插件侧:ServeIntroSpect做了什么
zoraxy_plugin库提供了ServeIntroSpect函数(实现于 zoraxy_plugin.go)。它检查命令行参数:当第一个参数是-introspect时,将插件声明的IntroSpect结构体以带缩进的 JSON 打印到 STDOUT 并退出(os.Exit(0)):
func ServeIntroSpect(pluginSpect *IntroSpect) { if len(os.Args) > 1 && os.Args[1] == "-introspect" { //Print the intro spect and exit jsonData, _ := json.MarshalIndent(pluginSpect, "", " ") fmt.Println(string(jsonData)) os.Exit(0) } }在插件main()中,这一调用位于最前面。以 helloworld 示例插件 为例:
func main() { // 打印 introspect 并在传入 -introspect 标志时退出 runtimeCfg, err := plugin.ServeAndRecvSpec(&plugin.IntroSpect{ ID: "com.example.helloworld", Name: "Hello World Plugin", Author: "foobar", AuthorContact: "admin@example.com", Description: "A simple hello world plugin", URL: "https://example.com", Type: plugin.PluginType_Utilities, VersionMajor: 1, VersionMinor: 0, VersionPatch: 0, UIPath: "/", // 工具类插件只提供 UI,不捕获流量 }) ... }ServeAndRecvSpec等价于先执行ServeIntroSpect(内省并退出),再执行RecvConfigureSpec(读取-configure配置,进入运行模式),是官方推荐的入口封装。
2. 手动触发:确认返回的 JSON 正确
-introspect支持手动触发,用于在发布前确认返回内容正确。文档给出的 debugger 示例插件 的验证输出如下:
$ ./debugger -introspect { "id": "org.aroz.zoraxy.debugger", "name": "Plugin Debugger", "author": "aroz.org", "author_contact": "https://aroz.org", "description": "A debugger for Zoraxy \u003c-\u003e plugin communication pipeline", "url": "https://zoraxy.aroz.org", "type": 0, "version_major": 1, "version_minor": 0, "version_patch": 0, "static_capture_paths": [ { "capture_path": "/test_a" }, { "capture_path": "/test_b" } ], "static_capture_ingress": "/s_capture", "dynamic_capture_sniff": "/d_sniff", "dynamic_capture_ingress": "/d_capture", "ui_path": "/debug", "subscription_path": "", "subscriptions_events": null }3. Zoraxy 侧:内省是如何被消费的
Zoraxy 在加载插件时会调用插件二进制并解析其 STDOUT 输出(introspect.go):
cmd := exec.CommandContext(ctx, entryPoint, "-introspect") output, err := cmd.Output() if ctx.Err() == context.DeadlineExceeded { return nil, fmt.Errorf("plugin introspect timed out") } ... err = json.Unmarshal(output, &pluginSpec)两个关键约束由此而来:
- 超时:Zoraxy 给内省调用设置了 10 秒超时(
context.WithTimeout(..., 10*time.Second))。如果插件在-introspect模式下启动缓慢(例如初始化时访问网络),会被判定为内省超时,编译"成功"但加载失败。 - 输出纯净:STDOUT 必须只包含 JSON 内省结果。
fmt.Println之类的调试输出如果出现在内省模式下,会导致json.Unmarshal失败。示例插件中调试打印都放在内省之后(如 debugger 的pathRouter.SetDebugPrintMode(true)),这是值得沿用的习惯。
另外,intrspect.go 中的checkSupportHotRebuild会检查插件目录是否存在Makefile或main.go,据此决定该插件是否支持热重建,这直接影响后续的编译工作流(见第五节)。
四、IntroSpect结构:内省 JSON 的字段契约
内省返回的结构体定义在zoraxy_plugin库中,Zoraxy 与插件共同使用。以当前仓库 zoraxy_plugin.go 的实现为准,完整字段如下:
| 字段 | JSON 键 | 说明 |
|---|---|---|
ID | id | 插件唯一 ID,建议使用反写域名,如com.yourdomain.pluginname |
Name | name | 插件名称 |
Author | author | 作者名 |
AuthorContact | author_contact | 作者联系方式,如邮箱 |
Description | description | 插件描述 |
URL | url | 插件主页 URL |
Type | type | 插件类型:0Router(路由插件)或1Utilities(工具插件) |
VersionMajor/Minor/Patch | version_major/minor/patch | 主/次/修订版本号 |
StaticCapturePaths | static_capture_paths | 静态捕获路径列表(capture_path),插件启用后这些规则恒定作用于 HTTP 代理规则,速度快但灵活性低 |
StaticCaptureIngress | static_capture_ingress | 静态捕获入口路径(如/s_handler) |
DynamicCaptureSniff | dynamic_capture_sniff | 动态捕获嗅探路径(如/d_sniff),Zoraxy 把请求转发到这里,插件返回 280 才捕获流量 |
DynamicCaptureIngress | dynamic_capture_ingress | 动态捕获入口路径(如/d_handler) |
UIPath | ui_path | 插件 UI 路径(如/ui),Zoraxy Web UI 会把该子路径整棵代理到插件 |
SubscriptionPath | subscription_path | 订阅事件路径(如/notifyme),事件触发时 Zoraxy 以SubscriptionEvent为 body 发送 POST |
SubscriptionsEvents | subscriptions_events | 订阅事件映射,键为事件名、值为事件说明 |
PermittedAPIEndpoints | permitted_api_endpoints | 插件允许访问的 Zoraxy API 端点列表(方法、端点、原因),用于插件向 Zoraxy 发起 API 调用 |
其中PluginType与ControlStatusCode的取值定义同样来自该库:
type PluginType int const ( PluginType_Router PluginType = 0 // 路由插件:处理/路由/转发流量 PluginType_Utilities PluginType = 1 // 工具插件:不拦截 dpcore 流量,如 Zerotier、静态 Web 服务器 ) const ( ControlStatusCode_CAPTURED ControlStatusCode = 280 // 流量被插件捕获 ControlStatusCode_UNHANDLED ControlStatusCode = 284 // 插件未处理,交由下一个插件 ControlStatusCode_ERROR ControlStatusCode = 580 // 处理出错,交给 Zoraxy 处理并记录日志 )想深入理解静态捕获与动态捕获的语义差异,可继续阅读 4. Capture Modes.md;对应路由实现见 static_router.go 与 dynamic_router.go。
五、编译产物的部署与热重建
1. 手动安装测试
编译并验证通过后,将二进制放入 Zoraxy 安装目录下的/plugins/{plugin_name}/文件夹即可手动安装(见 1. What is Zoraxy Plugin.md)。注意:文件夹内的二进制名称必须与插件文件夹名完全一致——放在/plugins/foobar/中的二进制应命名为foobar(Windows 为foobar.exe),不能叫foobar_plugin.exe之类的名字,否则插件无法被正确识别。
2. Zoraxy 自动重建(Hot Rebuild)
对于开发中的插件,Zoraxy 提供自动重建能力。hotrebuild.go 的RebuildPlugin逻辑为:
- 插件目录含
Makefile时,执行make构建; - 否则要求目录含
main.go且系统安装有 Go 编译器,执行go build -o <目录名>; - 重建前若插件正在运行则先停止,重建成功后若之前处于启用状态则自动重启并刷新标签映射。
与此配套的是 development.go 中的热重载(Hot Reload)机制:Zoraxy 周期性(默认HotReloadInterval秒)对插件入口二进制计算 SHA-256 哈希,一旦发现文件变更就自动调用HotReloadPlugin完成"停止 → 从磁盘重载最新版本"的闭环,间隔通过 API 可调且最小为 1 秒。这意味着编译脚本(如go build覆盖二进制)与 Zoraxy 热重载配合,可以实现改代码后自动生效的开发循环,无需手工重启。
3. 批量构建示例工程
仓库 build_all.sh 展示了多插件工程的批量构建流程:先将src/mod/plugins/zoraxy_plugin最新版复制到每个插件的mod/目录,再逐个执行go mod tidy与go build,任一失败则整体返回非零退出码。单插件开发时,保持mod/zoraxy_plugin与主仓库同步是避免协议不匹配的关键。
六、构建验证清单与常见问题
结合本文所有要点,给出编译与验证插件的最终检查清单:
- 目录结构完整:插件根目录含
main.go、go.mod,且mod/zoraxy_plugin/目录完整(含zoraxy_plugin.go等文件); go mod tidy无报错:有第三方依赖时确认网络可达、go.sum生成;go build成功:二进制默认以插件目录名命名;-introspect输出为纯 JSON:直接运行./<你的插件名> -introspect,确认输出能被json.Unmarshal解析,且id唯一、type取值正确;- 内省耗时在 10 秒内:Zoraxy 侧内省调用有 10 秒超时(introspect.go),避免在启动路径中做阻塞性网络操作;
- 部署命名匹配:放入
/plugins/{name}/的二进制文件名与文件夹名一致; - 运行模式自测:插件进入运行模式后监听
127.0.0.1:<port>(端口由 Zoraxy 通过-configure注入的ConfigureSpec提供,见 3. Configure.md),确认 UI 路径与捕获路径可访问。
七、延伸阅读
- 编译与内省协议定义:zoraxy_plugin.go
- 内省调用与热重建检测:introspect.go、hotrebuild.go
- 完整可编译示例:helloworld、debugger、restful-example、api-call-example
- 插件内省机制详解:2. Introspect.md
- 配置注入机制详解:3. Configure.md
- 插件架构总览:1. Plugin Architecture.md
- 后端
【免费下载链接】zoraxy
A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!
相关推荐
Termux编译构建:从源码编译应用和插件
Termux编译构建:从源码编译应用和插件 引言:告别"无法安装"的困境 你是否曾因Google Play版本限制而无法使用Termux的完整功能?是否在调试插
移动开发CLI操作系统Zoraxy 插件信息面板完全指南:从 IntroSpect 元数据到运行时洞察
Zoraxy 插件信息面板完全指南:从 IntroSpect 元数据到运行时洞察 导读 本篇指南聚焦 Zoraxy 反向代理中插件信息查看功能的完整使用与底层实
后端NVD3 Node.js/CommonJS 构建与验证指南:从源码编译到浏览器测试
NVD3 Node.js/CommonJS 构建与验证指南:从源码编译到浏览器测试 NVD3 是一款基于 d3.js 的可复用图表库,其核心构建产物 build
数据可视化前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考