# gohttp-plugin **Repository Path**: kingecg/gohttp-plugin ## Basic Information - **Project Name**: gohttp-plugin - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-04 - **Last Updated**: 2026-08-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # gohttp-plugin gohttp-plugin 是 [gohttpd](https://gitee.com/kingecg) 的插件框架,用于将 HTTP 服务以独立进程(插件)的形式动态加载到 gohttpd 中。插件通过 [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin) 的 net/rpc 协议与宿主进程通信。 ## 特性 - **独立进程隔离**:每个插件是一个独立的可执行二进制文件,崩溃不会影响宿主进程。 - **多服务支持**:单个插件进程可同时服务多个 `(ServerName, Path)` 组合,每个组合可携带不同的配置。 - **自动健康监控**:宿主进程持续监控插件进程状态,进程退出后自动以指数退避策略重启。 - **热加载**:插件目录中的可执行文件在宿主启动时自动扫描加载。 - **反向代理**:宿主将匹配的请求反向代理到插件内部的 HTTP 服务端口。 ## 架构 ``` ┌─────────────────────────────┐ │ gohttpd 宿主 │ │ │ │ ┌───────────────────────┐ │ │ │ Plugin Manager │ │ │ │ (plugin.go) │ │ │ │ - 加载/注册/重启/关闭 │ │ │ │ - 健康监控 │ │ │ │ - 反向代理 │ │ │ └──────────┬────────────┘ │ │ │ net/rpc │ └─────────────┼───────────────┘ │ ▼ ┌─────────────────────────────┐ │ 插件进程 (独立二进制) │ │ │ │ ┌───────────────────────┐ │ │ │ PluginServer (sdk) │ │ │ │ - Meta/Start/ShutDown │ │ │ │ - Serve/UnServe │ │ │ │ - 内部 HTTP 服务 │ │ │ └───────────────────────┘ │ └─────────────────────────────┘ ``` ### 插件加载流程 1. 宿主启动时调用 `LoadPlugins(PluginRoot)` 扫描插件目录。 2. 对每个可执行文件,通过 go-plugin 启动插件进程。 3. 宿主通过 RPC 调用 `Meta()` 获取插件名称。 4. 宿主通过 RPC 调用 `Start()` 初始化插件资源。 5. 健康监控 goroutine 监视进程退出并自动重启。 ### 路由分发流程 当路由引用某个插件时: 1. `GetHandler(serverName, path, name, options)` 返回反向代理。 2. 若该 `(serverName, path)` 服务尚未启动,宿主调用 `Serve()` 启动它。 3. 插件在 `127.0.0.1:0`(随机端口)启动内部 HTTP 服务并返回实际端口。 4. 宿主创建指向该端口的反向代理,将匹配请求转发过去。 ## 目录结构 ``` gohttp-plugin/ ├── plugin.go # 宿主端插件管理器(加载、注册、重启、健康监控、反向代理) ├── protocol/ │ ├── protocol.go # GoHttpPlugin 接口定义(宿主与插件共享的契约) │ └── rpc.go # net/rpc 服务端/客户端封装及 go-plugin 适配器 ├── sdk/ │ └── server.go # 插件开发 SDK(PluginServer、HandlerFactory、Serve 入口) ├── cmd/ │ └── my-plugin/ │ └── main.go # 示例插件(demo) ├── go.mod ├── go.sum └── LICENSE ``` ## 快速开始 ### 编写一个插件 插件是一个独立的 Go 程序,使用 `sdk` 包实现 `protocol.GoHttpPlugin` 接口。 ```go package main import ( "fmt" "net/http" "gitee.com/kingecg/gohttp-plugin/sdk" ) func main() { srv := &sdk.PluginServer{ Name: "my-plugin", Version: "1.0.0", HandlerFactory: func(config map[string]interface{}) http.Handler { greeting := "Hello from plugin!" if g, ok := config["greeting"].(string); ok { greeting = g } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, greeting) }) }, } sdk.Serve(srv) } ``` ### 构建插件 ```bash go build -o plugins/my-plugin ./cmd/my-plugin/ ``` ### 配置 gohttpd 在 gohttpd 的配置文件中指定插件目录和路由: ```json { "plugin_root": "./plugins", "servers": [{ "name": "demo", "port": 8080, "paths": [{ "path": "/my-plugin", "plugin_name": "my-plugin", "plugin_options": { "greeting": "Hello from path A!" } }, { "path": "/my-plugin-b", "plugin_name": "my-plugin", "plugin_options": { "greeting": "Hello from path B!" } }] }] } ``` 同一个插件进程即可同时服务两个路径,且返回不同的问候语。 ## SDK 使用指南 ### PluginServer `PluginServer` 是插件端的主要辅助类型,实现了 `protocol.GoHttpPlugin` 接口。插件作者设置以下字段后调用 `sdk.Serve()` 即可: | 字段 | 类型 | 说明 | |------|------|------| | `Name` | `string` | 插件名称,用于 `HttpPath.PluginName` 配置 | | `Version` | `string` | 插件版本号(仅信息用途) | | `Handler` | `http.Handler` | 静态 HTTP 处理器,所有请求都转发给它 | | `HandlerFactory` | `HandlerFactory` | 根据配置创建 HTTP 处理器的工厂函数,支持多服务不同配置 | > **注意**:`HandlerFactory` 优先于 `Handler`。若两者都未设置,插件将返回 404。 ### HandlerFactory ```go type HandlerFactory func(config map[string]interface{}) http.Handler ``` `Serve()` 为每个不同的 `(serverName, path)` 组合调用一次 `HandlerFactory`,传入该组合的配置,返回对应的处理器。这使得单个插件进程可以同时服务多个路径,且每个路径使用不同的配置。 ### 生命周期方法 插件作者可通过覆盖 `PluginServer` 的方法实现自定义生命周期逻辑: | 方法 | 调用时机 | 说明 | |------|---------|------| | `Meta()` | 插件加载时 | 返回插件名称和版本 | | `Start()` | 插件加载时(Meta 之后) | 初始化插件自身资源 | | `Serve(serverName, path, config)` | 路由首次引用时 | 启动内部 HTTP 服务,返回端口 | | `UnServe(serverName, path)` | 路由移除或配置变更时 | 停止内部 HTTP 服务 | | `ShutDown()` | 插件关闭时 | 释放插件资源并停止所有服务 | ## 宿主端 API(plugin.go) 宿主端(gohttpd)通过 `plugin` 包管理插件: | 函数/方法 | 说明 | |-----------|------| | `LoadPlugins(pluginRoot)` | 扫描目录并加载所有可执行插件 | | `GetManager()` | 获取全局插件管理器单例 | | `Manager.GetHandler(serverName, path, name, options)` | 获取插件的反向代理处理器 | | `Manager.UnServe(serverName, path, name)` | 停止指定服务 | | `Manager.RestartPlugin(name)` | 手动重启插件 | | `Manager.Shutdown()` | 优雅关闭所有插件 | ### 健康监控与自动重启 每个插件实例启动后,`monitorHealth()` goroutine 每 5 秒检查一次插件进程状态。若进程退出: 1. 等待退避时间(初始 1 秒,指数递增,最大 30 秒)。 2. 调用 `RestartPlugin()` 重启插件进程。 3. 重启成功后,自动重新服务所有已注册的 `(serverName, path)` 组合(使用保存的配置)。 4. 退避时间重置为 1 秒。 ## 协议(protocol 包) `protocol` 包定义了宿主与插件之间的共享契约,双方都导入此包。 ### GoHttpPlugin 接口 ```go type GoHttpPlugin interface { Meta() (PluginMeta, error) Start() error ShutDown() error Serve(ServerName, Path string, Config map[string]interface{}) (int, error) UnServe(ServerName, Path string) error } ``` ### RPC 通信 - `GoHttpPluginRPCServer`:运行在插件进程内,包装真实的实现并通过 net/rpc 暴露。 - `GoHttpPluginRPCClient`:运行在宿主进程内,通过 net/rpc 调用远程插件。 - `GoHttpPluginPlugin`:go-plugin 适配器,注册在插件集合中,名称固定为 `"gohttp-plugin"`。 ### 握手配置 宿主与插件使用相同的握手配置: ```go plugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "GOHTTPD_PLUGIN", MagicCookieValue: "v1", } ``` 通信协议为 `net/rpc`(`AllowedProtocols: [plugin.ProtocolNetRPC]`)。 ## 插件加载规则 - 插件目录中的**可执行文件**会被加载为插件。 - 目录、非可执行文件会被静默跳过。 - `.so` 文件(旧插件格式)会被跳过。 - 插件名称(`Meta()` 返回值)必须唯一,重复名称会导致加载失败。 - 插件名称不能为空。 ## 依赖 - [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin) v1.8.0 — 插件进程管理 - [gologger](https://git.kingecg.top/kingecg/gologger) — 日志 ## 许可证 [MIT](LICENSE)