feat: per-forward worker 模型 — 每条转发独立 frpc 配置+独立进程

核心改动(ring 协议不变,Task 本来就是 {Local,Remote,Link} 三元组):
- process.WorkerKey: worker 键改为 'local~remote~port' 三元组
- renderForward: 每条 forward 渲染只含 1 个 proxy 的独立 frpc 配置
  (替代 renderRemote 把该 remote 全部 forwards 塞进一个进程)
- ClaimFn/RevokeFn: 认领/撤销只操作这一条 forward 自己的进程
- SyncWorkers/auto-start: 只拉起 localOnly 转发的独立进程,
  集群转发由 ring 认领节点拉起 (消除三台争抢 proxy already exists)
- RemoteStatus/StartRemote/StopRemote/RestartRemote: remote 级聚合辅助,
  保持 /profiles API 形状不变, 前端零改动
- handleStatus/logs: 按 worker key 解析真实 remote 名

效果: 三台节点的 frpc 各自只注册自己拥有的 proxy, 单条转发故障不再波及兄弟
This commit is contained in:
JianFeeeee
2026-08-24 01:42:58 +08:00
parent 2292ee7f3a
commit e3a978888a
9 changed files with 340 additions and 101 deletions

View File

@ -9,6 +9,7 @@ import (
"time"
"webui4frpc/internal/cluster"
"webui4frpc/internal/process"
"webui4frpc/internal/store"
)
@ -76,8 +77,11 @@ func (h *Handler) handleLocalByName(w http.ResponseWriter, r *http.Request) {
case http.MethodDelete:
if l.LocalOnly {
if h.Process != nil {
// Per-forward model: stop every localOnly worker of this local.
if fwd, _ := h.Store.LinksForLocal(name); len(fwd) > 0 {
_ = h.Process.Stop(fwd[0].Remote)
for _, f := range fwd {
_ = h.Process.Stop(process.WorkerKey(name, f.Remote, f.RemotePort))
}
}
}
} else if h.Ring != nil {