☰
Zoraxy 插件编译指南:从 Go 源码构建到 `-introspect` 验证
2026/10/12 1:23:11 网站建设 项目流程
  • 后端

【免费下载链接】zoraxy

A general purpose HTTP reverse proxy and forwarding tool. Now written in Go!

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

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

![Zoraxy 插件三步工作流程:Introspect → Configure → Forwarding,编译产物的验证环节正是第一步 Introspect](https://raw.gitcode.com/gh_mirrors/zo/zoraxy/raw/fd8eb763b943bb1dbedff39c0ef82cdef5e57aa6/docs/plugins/docs/2. Architecture/img/1. Plugin Architecture/plugin_workflow.png?utm_source=gitcode_repo_files)

一、编译前的准备:插件就是"会说话"的 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.go

example/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 字符串打印到 STDOUT

1.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 键说明
IDid插件唯一 ID,建议使用反写域名,如com.yourdomain.pluginname
Namename插件名称
Authorauthor作者名
AuthorContactauthor_contact作者联系方式,如邮箱
Descriptiondescription插件描述
URLurl插件主页 URL
Typetype插件类型:0Router(路由插件)或1Utilities(工具插件)
VersionMajor/Minor/Patchversion_major/minor/patch主/次/修订版本号
StaticCapturePathsstatic_capture_paths静态捕获路径列表(capture_path),插件启用后这些规则恒定作用于 HTTP 代理规则,速度快但灵活性低
StaticCaptureIngressstatic_capture_ingress静态捕获入口路径(如/s_handler)
DynamicCaptureSniffdynamic_capture_sniff动态捕获嗅探路径(如/d_sniff),Zoraxy 把请求转发到这里,插件返回 280 才捕获流量
DynamicCaptureIngressdynamic_capture_ingress动态捕获入口路径(如/d_handler)
UIPathui_path插件 UI 路径(如/ui),Zoraxy Web UI 会把该子路径整棵代理到插件
SubscriptionPathsubscription_path订阅事件路径(如/notifyme),事件触发时 Zoraxy 以SubscriptionEvent为 body 发送 POST
SubscriptionsEventssubscriptions_events订阅事件映射,键为事件名、值为事件说明
PermittedAPIEndpointspermitted_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与主仓库同步是避免协议不匹配的关键。

六、构建验证清单与常见问题

结合本文所有要点,给出编译与验证插件的最终检查清单:

  1. 目录结构完整:插件根目录含main.go、go.mod,且mod/zoraxy_plugin/目录完整(含zoraxy_plugin.go等文件);
  2. go mod tidy无报错:有第三方依赖时确认网络可达、go.sum生成;
  3. go build成功:二进制默认以插件目录名命名;
  4. -introspect输出为纯 JSON:直接运行./<你的插件名> -introspect,确认输出能被json.Unmarshal解析,且id唯一、type取值正确;
  5. 内省耗时在 10 秒内:Zoraxy 侧内省调用有 10 秒超时(introspect.go),避免在启动路径中做阻塞性网络操作;
  6. 部署命名匹配:放入/plugins/{name}/的二进制文件名与文件夹名一致;
  7. 运行模式自测:插件进入运行模式后监听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!

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

相关推荐

上一篇:Iris Admin:一个功能丰富的Go语言Web管理框架
下一篇:CEF源码编译完全指南:从下载到构建的详细步骤

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

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

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

立即咨询