Documentation
¶
Index ¶
- Constants
- Variables
- func CheckDockerAvailable(ctx context.Context) (string, error)
- func ComposeEnv(base []string, spec CommandSpec) []string
- func DefaultVerifyProcessOwnership(pid int, instanceUUID, workDir string, expectWrapper bool) bool
- func PullDockerImage(ctx context.Context, ref string) error
- func RemoveDockerImage(ctx context.Context, ref string, force bool) error
- type CommandSpec
- type CrashInfo
- type ForeignRuntime
- type HealthPolicy
- type HealthReport
- type HealthScanner
- type IProcessCommand
- type ImageSummary
- type Instance
- type InstanceEvidence
- type InstanceSnapshot
- type InstanceState
- type ManagedContainer
- type Manager
- func (m *Manager) AdoptForeignRuntime(uuid string) (int, error)
- func (m *Manager) Create(uuid, name, startCommand, stopCommand, workDir string, ...) error
- func (m *Manager) GetAllInstanceStates() []InstanceSnapshot
- func (m *Manager) GetInstance(uuid string) (*Instance, bool)
- func (m *Manager) GetInstanceDockerStats(ctx context.Context, uuid string) (cpuPercent float64, memBytes int64, memLimitBytes int64, ok bool)
- func (m *Manager) GetInstancePID(uuid string) int
- func (m *Manager) GetRCONConfig(uuid string) (port int, password string, err error)
- func (m *Manager) GetState(uuid string) (InstanceState, error)
- func (m *Manager) Kill(uuid string) error
- func (m *Manager) ListInstances() []string
- func (m *Manager) PreflightStart(uuid string) ([]PreflightCheckResult, error)
- func (m *Manager) ProbeInstanceEvidence(uuids []string) []InstanceEvidence
- func (m *Manager) ReapDaemonForDelete(uuid string)
- func (m *Manager) RecoverDaemonInstances() (int, error)
- func (m *Manager) ReleaseCircuitBreaker(uuid string) (reason string, released bool)
- func (m *Manager) Remove(uuid string) error
- func (m *Manager) Restart(uuid string) error
- func (m *Manager) RestartIfRunning(uuid string) error
- func (m *Manager) SendCommand(uuid, command string) error
- func (m *Manager) SetCrashHandler(handler func(instanceID string, info CrashInfo))
- func (m *Manager) SetDockerConfig(uuid, image string, mappings []PortMapping, cpuLimit float64, ...)
- func (m *Manager) SetForeignRuntimes(byUUID map[string]ForeignRuntime)
- func (m *Manager) SetGracefulStopTimeout(uuid string, seconds int)
- func (m *Manager) SetInstanceStartHandler(handler func(instanceID string))
- func (m *Manager) SetLaunchConfig(uuid, startCommand, jdkPath, jdkBinPath string, envVars map[string]string, ...)
- func (m *Manager) SetMemGuard(cfg MemGuardConfig)
- func (m *Manager) SetMemReader(r readSysMem)
- func (m *Manager) SetOrphanAuditHandler(handler func(action, targetID, detail string, success bool, errMsg string))
- func (m *Manager) SetOutputHandler(handler func(instanceID string, stream string, data []byte))
- func (m *Manager) SetProbePort(uuid string, port int)
- func (m *Manager) SetQueryPort(uuid string, port int)
- func (m *Manager) SetRCONConfig(uuid string, port int, password string) error
- func (m *Manager) SetResourceLimits(uuid string, cpuLimit float64, memLimitMB int64)
- func (m *Manager) SetServerPort(uuid string, port int)
- func (m *Manager) SetStateChangeHandler(handler func(instanceUUID string, oldState, newState InstanceState))
- func (m *Manager) Start(uuid string) error
- func (m *Manager) Stop(uuid string) error
- func (m *Manager) StopAll()
- type MemGuardConfig
- type OrphanDisposePolicy
- type OrphanFinding
- type OrphanKind
- type OrphanScanner
- type PortMapping
- type PreflightCheckResult
- type ProcessType
- type ScannedProcess
Constants ¶
const ( // HealthFaultNone 健康(或状态瞬态,无需标注)。 HealthFaultNone = "" // HealthFaultSuspected 假死嫌疑累积中(存活但响应探测失败,尚未达阈值)。 HealthFaultSuspected = "suspected_dead" // HealthFaultDead 已确认假死(连续 N 次存活但不响应)。 HealthFaultDead = "dead" // HealthFaultCrash 进程/容器不存活(崩溃,交由既有处理)。 HealthFaultCrash = "crashed" // HealthFaultCircuitBroken 已熔断(窗口内重启次数超限,停止自动重启,等待人工确认)。 HealthFaultCircuitBroken = "circuit_broken" // HealthFaultHealthy 明确健康(存活 ∧ 响应,或无可探维度仅存活):供 CP 清空历史 status_reason。 HealthFaultHealthy = "healthy" )
健康故障标识(经心跳上报 CP,写入 instances.status_reason / 健康位)。
const ( // HealthProbeTCP 以 TCP connect 服务端口判响应(未部署探针的兜底维度)。 HealthProbeTCP = "tcp" // HealthProbeHTTP 以 HTTP GET ProbePort 判响应(ServerProbe /metrics)。 HealthProbeHTTP = "http" )
响应维度探针类型(可配,spec §2.3 probeKind)。
const ( // HealthActionWarn 仅告警 + 落审计 + 标原因(默认)。 HealthActionWarn = "warn" // HealthActionRestart 走既有优雅重启路径自愈。 HealthActionRestart = "restart" // HealthActionCircuitBreak 熔断(非可配动作,作为审计/上报动作标识)。 HealthActionCircuitBreak = "circuit_break" )
假死自愈动作(spec §2.2)。
const ( // DefaultHealthScanInterval 默认巡检周期(spec §2.1)。 DefaultHealthScanInterval = 30 * time.Second // DefaultSuspicionThreshold 默认连续假死判定阈值(spec §2.2)。 DefaultSuspicionThreshold = 3 // DefaultCircuitBreakerThreshold 默认崩溃熔断阈值(窗口内重启次数,spec §2.2)。 DefaultCircuitBreakerThreshold = 5 // DefaultCircuitBreakerWindow 默认崩溃熔断滚动窗口(spec §2.2)。 DefaultCircuitBreakerWindow = 10 * time.Minute // DefaultStartupWarmup 默认启动宽限期:Start 返回 RUNNING 后的这段时间内不做假死判定, // 避免慢启动 MC(World 加载/模组初始化常达 90s+)被连续探针失败误判并反复重启(FR-459 复审项 2)。 DefaultStartupWarmup = 5 * time.Minute // DefaultSelfHealMaxRestarts 假死自愈在一熔断窗口内的重启次数上限(避免假死路径重启风暴,FR-459 复审项 7)。 DefaultSelfHealMaxRestarts = 3 )
巡检默认值。
导出的 Default* 是**唯一真源**:worker 配置层(internal/worker/config.go)经 process 包引用, 避免「本地默认」与「进程归一默认」两处字面量漂移(FR-459 复审项 12)。
Variables ¶
var ErrNotImplemented = errors.New("该启动方式尚未实现")
ErrNotImplemented 表示该启动方式尚未实现。 docker/rcon 等未落地的策略统一返回此错误,避免误用。
Functions ¶
func CheckDockerAvailable ¶
CheckDockerAvailable 探测本机 Docker 守护进程可用性(FR-237):连客户端 + ServerVersion。 返回 Engine 版本;守护进程未装 / 未起 / 不可达时返回 error。供创建向导选 docker 模式前先测。
func ComposeEnv ¶
func ComposeEnv(base []string, spec CommandSpec) []string
ComposeEnv 合成进程最终环境: 1) Worker 自身环境(os.Environ)作为基线,保留系统 PATH/编码等; 2) 注入 JAVA_HOME(如果提供)和对应 PATH 前缀; 3) 叠加实例自定义 EnvVars(覆盖基线同名键)。 始终不修改调用方的 map。
func DefaultVerifyProcessOwnership ¶ added in v0.23.0
DefaultVerifyProcessOwnership 以进程侧证据判定 pid 是否确属 instanceUUID 对应的受管进程(FR-455①)。
处置前置存活复核的核心:任何强杀(killTree)之前都必须先确认这个 PID「确属目标实例」, 否则若 PID 已被 OS 复用给无关进程(机器负载高时真实存在),就会误杀无关进程。 与「是否还活着」(daemon.IsPIDAlive)正交:本函数回答的是「是否确属目标实例」。
保守取向(「宁可多等一轮,不可误杀」):任何无法确认(进程不在、cmdline 读不到、权限不足) 一律返回 false,调用方据此只告警不处置。判定采用「多特征任一命中」的宽口径,兼顾 wrapper 与 Java:
- cmdline 含实例 UUID(部分部署把 UUID 写进启动参数);
- cmdline 含实例工作目录绝对路径,或进程 cwd 等于工作目录(Java 常以工作目录为 cwd);
- expectWrapper 时,进程命令行形如 jianmanager worker 的 `... daemon`,**且**该进程环境 (/proc/<pid>/environ)确携带本实例 UUID(FR-456 F11)。
F11:wrapper 由 `exec.Command(exe, "daemon")` 启动,实例 UUID 只在 env (daemon.EnvWrapperConfig → JM_DAEMON_WRAPPER_CONFIG JSON)而不在 argv——argv 对**任何**实例 的 wrapper 都相同。若仅按 argv「... daemon」判真,则节点上所有 wrapper 都会被判为「本实例」, 一旦 PID 被复用/串号即误杀他人 wrapper。故 wrapper 分支必须再校验进程环境的实例 UUID, 单靠 argv 不再单独判真;环境读不到(权限/平台不支持)时保守返回 false(不杀)。
匹配口径的误判率需真机标定(spec §5):过宽会放过真孤儿,过窄会误拦截。
func PullDockerImage ¶
PullDockerImage 从 registry 拉取镜像到 Worker 本机。 拉取流必须读尽,否则 Docker 守护进程可能中断拉取。
Types ¶
type CommandSpec ¶
type CommandSpec struct {
UUID string
Name string
StartCommand string
// StopCommand 优雅停止时写入进程 stdin 的命令(按实例角色派生:MC 后端 stop / 代理 end)。
// 空时由策略回退到默认 "stop"。仅 daemon 策略经 stdin 优雅关服时使用。
StopCommand string
WorkDir string
EnvVars map[string]string
// JavaHome 显式指定 JAVA_HOME(来自实例绑定的 JDK),非空时 Worker 会
// 把它注入到进程环境并把 JavaHome/bin 接入 PATH。
JavaHome string
// JDKBinPath 显式指定要前置到 PATH 的目录;空时由 JavaHome 派生。
JDKBinPath string
AutoRestart bool
ProcessType ProcessType
// ProbePort 是实例 ServerProbe /metrics 端口(CP 分配后下发)。daemon 策略透传到
// wrapper→PID 记录,供 Worker 重启恢复后心跳继续自采(FR-060);0=未部署探针。
ProbePort int
// GracefulStopTimeoutSeconds 是优雅停止超时(秒,CP 从平台设置下发,FR-063)。daemon 策略
// 透传到 wrapper 做超时强杀兜底;0=未指定,wrapper 回退 env/默认。值在启动时定型。
GracefulStopTimeoutSeconds int
// Image 是 docker 模式的容器镜像引用(如 itzg/minecraft-server:latest)。
// 仅 docker 策略使用;缺失时 docker 启动报错(ADR-019)。
Image string
// PortMappings 是 docker 模式的容器端口↔宿主端口映射(宿主端口来自 FR-032 端口池)。
// 仅 docker 策略使用,发布容器内端口到宿主(ADR-019)。
PortMappings []PortMapping
// CPULimit 是 docker 模式的 CPU 核数上限(如 1.5 表示 1.5 核),注入 HostConfig.NanoCPUs。
// 仅 docker 策略使用;0=不限制(FR-079,见 ADR-019)。
CPULimit float64
// MemLimitMB 是 docker 模式的内存上限(MiB),注入 HostConfig.Memory。
// 仅 docker 策略使用;0=不限制(FR-079,见 ADR-019)。
MemLimitMB int64
// DiskLimitMB 是 docker 模式的磁盘上限(MiB),仅持久化与展示,v1 不注入 HostConfig。
// bind-mount 工作目录无法用 HostConfig 简单施加磁盘配额(依赖存储驱动),留作前向兼容(FR-079)。
DiskLimitMB int64
}
CommandSpec 是启动一个实例所需的全部配置。 各策略实现(direct/daemon/docker)从同一份配置派生各自所需参数。
type CrashInfo ¶
type CrashInfo struct {
// ExitCode 进程退出码;无法获知(Wait 出错无退出状态 / 容器 Wait 错误)时为 -1。
ExitCode int
// Signal 终止信号名(Unix,如 killed);Windows / 非信号退出为空。
Signal string
// DurationMs 本次运行时长(毫秒)。
DurationMs int64
// OOMKilled 容器被 cgroup OOM killer 终止(FR-467)。仅 docker 模式可判定:
// 容器退出码恒为 137 且宿主侧无信号,只有 ContainerInspect 的 State.OOMKilled 能给出这个事实。
OOMKilled bool
// OccurredAt 崩溃发生时刻。
OccurredAt time.Time
}
CrashInfo 进程非正常退出的现场信息(FR-313):退出码 ≠ 0,或 RUNNING/STARTING 态 意外退出时由策略捕获,经 Manager 崩溃回调扇出、组装崩溃快照上报 CP。
type ForeignRuntime ¶ added in v0.23.0
type ForeignRuntime struct {
// PID 该外来活进程的 PID。
PID int
// Cmdline 该进程命令行摘要(已截断,供运维识别来源)。
Cmdline string
}
ForeignRuntime 一条「已注册实例工作目录下的外来活进程」观测(FR-471)。
背景:服务器可能被运维在平台之外启动(真机场景:/home/<user>/server 下 60 台农场由 tmux 拉起), 平台 DB 记 STOPPED 而磁盘进程在跑。扫描观测到这种「记账与磁盘不一致」后写入本缓存, 经心跳上报 CP 供面板标红与人工「接管」,本身**不处置**任何进程。
type HealthPolicy ¶ added in v0.23.0
type HealthPolicy struct {
// Enabled 是否执行巡检动作(本地开关 ∧ CP 开关,见 HealthScanner.effectivePolicy)。
Enabled bool
// ScanInterval 巡检周期(<=0 用默认 30s)。
ScanInterval time.Duration
// ProbeKind 响应维度探针类型:tcp / http(空=auto:有 server-port 用 tcp,否则有 probe-port 用 http)。
ProbeKind string
// SuspicionThreshold 连续判假死次数阈值(<=0 用默认 3)。
SuspicionThreshold int
// Action 假死动作:warn / restart(空/未知回退 warn)。
Action string
// CircuitBreakerThreshold 滚动窗口内重启次数阈值(<=0 用默认 5)。
CircuitBreakerThreshold int
// CircuitBreakerWindow 滚动窗口(<=0 用默认 10m)。
CircuitBreakerWindow time.Duration
// StartupWarmup 启动宽限期(<=0 用默认 5m;<0 表示显式关闭宽限——测试/特殊场景)。
StartupWarmup time.Duration
// SelfHealMaxRestarts 一熔断窗口内假死自愈重启次数上限(<=0 用默认 3)。
SelfHealMaxRestarts int
}
HealthPolicy 是 FR-459 巡检策略的 Worker 侧生效值。 本地基线来自 worker.yml(HealthScanConfig);CP 经心跳响应下发后覆盖 (enabled 以外的字段在 CP 下发存在时以 CP 为准)。
func DefaultHealthPolicy ¶ added in v0.23.0
func DefaultHealthPolicy() HealthPolicy
DefaultHealthPolicy 返回 FR-459 的内置默认策略。
type HealthReport ¶ added in v0.23.0
type HealthReport struct {
UUID string
// Liveness 进程/容器存活证据。
Liveness bool
// Readiness 响应探测是否可达(无可用探针端口时为 true——仅存活即健康)。
Readiness bool
// Healthy 存活 ∧ 响应。
Healthy bool
// Warmup 本轮处于启动宽限期(慢启动阶段不做假死判定,故 Readiness 不参与结论)。
Warmup bool
// Fault 当前健康故障标识(HealthFault*;空=健康)。
Fault string
// Reason 供 statusReason 的说明(空=无故障)。
Reason string
// Actions 本轮对实例执行的动作(warn / restart / circuit_break)。
Actions []string
}
HealthReport 单个实例的一轮巡检结论。
type HealthScanner ¶ added in v0.23.0
type HealthScanner struct {
// contains filtered or unexported fields
}
HealthScanner FR-459 运行期周期巡检器(仿 OrphanScanner:New... + Start(ctx) + ScanOnce)。
func NewHealthScanner ¶ added in v0.23.0
func NewHealthScanner(mgr *Manager, enabled bool, interval time.Duration, policy HealthPolicy) *HealthScanner
NewHealthScanner 构造巡检器。enabled 为本地开关,interval<=0 取默认 30s,policy 为空取默认。
func (*HealthScanner) ScanOnce ¶ added in v0.23.0
func (s *HealthScanner) ScanOnce() []HealthReport
ScanOnce 执行一轮巡检并(按策略)执行自愈动作。返回本轮结论(供单测/观测)。 开关关闭时不产生任何动作与结论(spec §4 验收 9)。
func (*HealthScanner) SetPolicy ¶ added in v0.23.0
func (s *HealthScanner) SetPolicy(policy HealthPolicy)
SetPolicy 应用 CP 下发的巡检策略(每次心跳调用、幂等)。enabled 由 localEnabled ∧ 下发值决定。
func (*HealthScanner) Start ¶ added in v0.23.0
func (s *HealthScanner) Start(ctx context.Context)
Start 启动周期巡检 goroutine(随 ctx 取消而退出)。mgr 为空或不启用时不启动。
用每轮重建的 timer 而非固定 ticker:使 CP 下发的 scan_interval 无需重启即生效 (interval 变化在下一轮起算)。
type IProcessCommand ¶
type IProcessCommand interface {
// Start 启动实例进程。ctx 用于取消启动阶段(如连接 wrapper 超时)。
Start(ctx context.Context) error
// Stop 优雅停止实例。
Stop() error
// Kill 强制终止实例。
Kill() error
// SendCommand 向实例 stdin 发送一行命令。
SendCommand(command string) error
// State 返回策略当前记录的实例状态。
State() InstanceState
// Close 释放策略持有的资源(socket 连接、goroutine 等),
// 不影响被管理的游戏服进程本身(daemon 模式下 wrapper 继续运行)。
Close() error
// GetPID 返回实例进程的 PID,用于从 OS 层采集进程内存等指标。
// 未启动或已退出时返回 0。
GetPID() int
}
IProcessCommand 进程启动策略接口。 Manager 按 instance.ProcessType 选择具体实现(direct/daemon/docker), 把「如何启动/停止/发送命令」的差异收敛到策略内部,Manager 只负责路由和生命周期记账。 参见 ADR-003: 守护进程 Wrapper 模式。
type ImageSummary ¶
ImageSummary 是 Worker 本机一个 Docker 镜像的精简描述(FR-078,ADR-019)。
func ListDockerImages ¶
func ListDockerImages(ctx context.Context) ([]ImageSummary, error)
ListDockerImages 列出 Worker 本机 Docker 镜像。 Docker 不可用时返回错误,调用方据此回报 docker_available=false(FR-078)。
type Instance ¶
type Instance struct {
UUID string
Name string
StartCommand string
// StopCommand 优雅停止命令(按角色派生:MC 后端 stop / 代理 end);空时回退默认 stop。
StopCommand string
WorkDir string
EnvVars map[string]string
JDKPath string
JDKBinPath string
RCONPort int
RCONPassword string
// ProbePort 是实例 ServerProbe /metrics 端口(CP 分配后随 Create 下发)。
// 心跳采集器据此自采每实例富指标(FR-060);0=未部署探针,心跳跳过该实例。
ProbePort int
// ServerPort 是实例 server-port(MC 游戏端口),用于 SLP 直探(FR-446);0=未知。
ServerPort int
// QueryPort 是实例 query.port(Query/GameSpy4 端口),用于 Query 直探(FR-446);0=未开启。
QueryPort int
// GracefulStopTimeoutSeconds 是优雅停止超时(秒,CP 从平台设置下发,FR-063)。daemon 启动时
// 透传到 wrapper 做超时强杀兜底;0=未指定,wrapper 回退 env/默认。值在启动时随 spec 定型。
GracefulStopTimeoutSeconds int
// Image 是 docker 模式的容器镜像引用(ADR-019);仅 docker 实例使用。
Image string
// PortMappings 是 docker 模式的容器端口↔宿主端口映射(ADR-019);仅 docker 实例使用。
PortMappings []PortMapping
// CPULimit / MemLimitMB / DiskLimitMB 是 docker 模式的资源限额(FR-079,见 ADR-019);
// 仅 docker 实例使用,值在 Start 时随 spec 定型。0=不限制;DiskLimitMB v1 仅记账不注入。
CPULimit float64
MemLimitMB int64
DiskLimitMB int64
State InstanceState
AutoRestart bool
CrashCount int
// StartedAt 是最近一次跨入 RUNNING 的时刻(FR-459 启动宽限期的纪元起点)。
// 零值=未知(PID 恢复等路径),此时巡检不做宽限、保持既有判定。
StartedAt time.Time
// contains filtered or unexported fields
}
Instance 运行中的实例记账信息。 策略实现(direct/daemon)持有进程/socket 句柄,这里只保留 Manager 路由与查询所需字段。
type InstanceEvidence ¶ added in v0.23.0
type InstanceEvidence struct {
UUID string
// Running 是主判据:进程/容器侧证据是否显示该实例仍在运行。
Running bool
// RootPID 证据来源根进程 PID(0=未知)。
RootPID int
// ProcessAlive 受管进程(wrapper 或 Java 或 direct 根进程)存活。
ProcessAlive bool
// SocketReachable daemon socket 可达(daemon 模式)。
SocketReachable bool
// ContainerRunning docker 容器在跑(docker 模式)。
ContainerRunning bool
// Detail 证据摘要(供 CP statusReason)。
Detail string
}
InstanceEvidence 单个实例的进程侧证据(FR-455③)。
type InstanceSnapshot ¶
type InstanceSnapshot struct {
UUID string
State string // STOPPED, STARTING, RUNNING, STOPPING, CRASHED
ProbePort int // ServerProbe /metrics 端口;>0 且 RUNNING 时心跳采集器自采富指标(FR-060)
ServerPort int // server-port(MC 游戏端口);>0 时心跳做 SLP 直探(FR-446)
QueryPort int // query.port;>0 时心跳做 Query 直探(FR-446)
PID int // 受管实例根进程 PID;>0 时心跳可采集进程 TOPN(FR-170)
// Health 是 FR-459 健康巡检结论标识(healthy/suspected_dead/dead/crashed/circuit_broken;空=无结论)。
// 经心跳上报 CP,供健康总览墙(FR-461)与动态基线(FR-462)消费。
Health string
// StatusReason 是健康巡检给出的原因说明(假死/熔断等,FR-459);空=正常。
StatusReason string
// ForeignPID 是「已注册实例工作目录下存在活进程、但本实例未认作运行」时该进程 PID(0=无漂移,FR-471)。
// 由孤儿扫描第 4 相观测、经 SetForeignRuntimes 缓存供本快照读取;仅观测,不表示平台已处置。
ForeignPID int
// ForeignCmdline 上述漂移进程的命令行摘要(已截断)。
ForeignCmdline string
}
InstanceSnapshot 表示单个实例的状态快照(用于心跳上报)。
type InstanceState ¶
type InstanceState string
InstanceState 实例运行状态。
const ( StateStopped InstanceState = "STOPPED" StateStarting InstanceState = "STARTING" StateRunning InstanceState = "RUNNING" StateStopping InstanceState = "STOPPING" StateCrashed InstanceState = "CRASHED" )
type ManagedContainer ¶ added in v0.23.0
type ManagedContainer struct {
UUID string
Name string // jianmanager-<uuid>
Running bool
PID int
// ManagedLabel 容器是否带平台受管标签(FR-456 N5,见 containerManagedLabelKey)。
ManagedLabel bool
// LabelInstanceUUID 受管标签内记录的实例 UUID,用于与容器名交叉核对(可能为空)。
LabelInstanceUUID string
}
ManagedContainer 本平台受管容器快照(供 docker 残留扫描;可由测试注入)。
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager 进程管理器。 它通过 IProcessCommand 策略接口支持多种启动方式(direct/daemon/docker), 参见 ADR-003: 守护进程 Wrapper 模式。
func (*Manager) AdoptForeignRuntime ¶ added in v0.23.0
AdoptForeignRuntime 接管实例工作目录下的外来活进程(FR-471)。
语义 = 「先让外来进程优雅退场,再以受管方式拉起」:对外来进程树发 SIGTERM(Paper 走 shutdown hook 保存世界后正常退出),有界等待其退出,超时升级 SIGKILL 强杀整棵进程树,随后按正常路径 Start, 使平台记账与磁盘事实对齐、此后由本平台掌管该实例生命周期。
与孤儿扫描(FR-456)的分工:那里处置的是**无主**进程(Worker 内存表已丢失该实例),默认只观测; 这里处置的是**已注册实例**目录下的进程,是人工确认后的显式接管动作,故允许停止进程。
返回被停止的外来进程 PID(0=接管前本无漂移,等价于一次正常启动)。 实例未注册返回 error;外来进程未能退出时不启动(避免双开),并落审计。
func (*Manager) Create ¶
func (m *Manager) Create(uuid, name, startCommand, stopCommand, workDir string, envVars map[string]string, autoRestart bool, processType ProcessType, jdkPath, jdkBinPath string, probePort, gracefulStopTimeoutSeconds int) error
Create 创建实例(但不启动)。processType 决定启动方式(direct/daemon/docker/rcon)。 jdkPath / jdkBinPath 非空时会被注入到实例启动时的环境。 stopCommand 为优雅停止命令(按角色派生:MC 后端 stop / 代理 end),空时回退默认 stop。 probePort 为实例 ServerProbe /metrics 端口(CP 分配),供心跳采集器自采富指标(FR-060);0=未部署。 gracefulStopTimeoutSeconds 为优雅停止超时(秒,CP 从平台设置下发,FR-063);0=未指定,wrapper 回退默认。
func (*Manager) GetAllInstanceStates ¶
func (m *Manager) GetAllInstanceStates() []InstanceSnapshot
GetAllInstanceStates 返回所有实例的状态快照(用于心跳上报)。
func (*Manager) GetInstance ¶
GetInstance 获取实例信息。
func (*Manager) GetInstanceDockerStats ¶
func (m *Manager) GetInstanceDockerStats(ctx context.Context, uuid string) (cpuPercent float64, memBytes int64, memLimitBytes int64, ok bool)
GetInstanceDockerStats 采集 docker 实例的容器资源指标(CPU%/内存/内存上限,字节)。 非 docker 实例、未运行或采集失败返回 ok=false,调用方据此回退 OS 进程内存路径。
func (*Manager) GetInstancePID ¶
GetInstancePID 获取实例进程的 PID。 策略未启动或已退出时返回 0。
func (*Manager) GetRCONConfig ¶
GetRCONConfig 获取实例的 RCON 配置。
func (*Manager) GetState ¶
func (m *Manager) GetState(uuid string) (InstanceState, error)
GetState 获取实例状态。
func (*Manager) ListInstances ¶
ListInstances 返回所有实例的 UUID 列表。
func (*Manager) PreflightStart ¶
func (m *Manager) PreflightStart(uuid string) ([]PreflightCheckResult, error)
PreflightStart 对已注册实例做启动前同步预检(FR-314):校验 java 运行时、工作目录、启动目标 jar, 供 CP 在转 STARTING 前同步拦截配置错误,终结「配置错误也要启动中→崩溃兜一圈」。 docker 实例整体放行(本地 JDK/文件语义不适用)。实例未注册返回 error。 与 Start 内嵌的 preflightJavaVersion 同源复用、并存不冲突(纵深防御,防 CP 绕过与竞态窗口)。
FR-471 在既有三项后补两项**现场占用**检查(docker 仍整体放行):
- work_dir_busy:工作目录下已有活进程(可能为未纳管进程或残留);
- port_free:实例 server-port 已被本机监听。
真机背景:外部启动的服务器在磁盘上跑、平台记 STOPPED,此时从面板点「启动」就会双开—— 撞 world/session.lock、抢端口、两个进程写同一批文件。这两项把双开拦在转 STARTING 之前。
func (*Manager) ProbeInstanceEvidence ¶ added in v0.23.0
func (m *Manager) ProbeInstanceEvidence(uuids []string) []InstanceEvidence
ProbeInstanceEvidence 返回一批实例的进程侧证据(FR-455③)。 对每个实例:优先用内存表策略的实时 PID;内存表无该实例(Worker 曾硬崩)时回退 PID 文件 / 容器名探测。
func (*Manager) ReapDaemonForDelete ¶
ReapDaemonForDelete 在删除实例前强杀该实例的 daemon 进程树并清理遗留的 PID 文件 / socket(FR-310)。
背景(真机缺陷):删除运行中 daemon 实例时,删除流程只杀 wrapper 组随即 RemoveAll 工作目录。 Unix 上被托管的 Java 经 wrapper 的 applyProcAttr 自成进程组(见 daemon.applyProcAttr),杀 wrapper 组够不到它;未死透的 Java 在 RemoveAll 之后继续写 world region 文件,把工作目录重建出来——于是 worker 日志打印「已清理工作目录」(RemoveAll 返回 nil),盘上却残留 world/plugins。此外 wrapper 被 强杀时其 defer cleanupPIDFile 不执行,遗留 <uuid>.pid 与 <uuid>.sock(本机 var/servers 下)。
处置:按 PID 记录强杀 wrapper 与 Java 两棵进程树——Unix 各杀其组,故两棵都要杀;Windows taskkill /T 已覆盖子树,补杀已死 PID 无害——轮询确认死透后再由调用方 RemoveAll,杜绝存活进程回写;确认死透即 清理遗留的 PID 文件与 socket。与 FR-325 接管兜底 reapOrphanWrapper 同源(复用 killTree / waitPIDsGone / pidAlive 桩,便于确定性单测)。
无 PID 文件(非 daemon 实例,或 wrapper 已优雅退出自清理)时为空操作。强杀后仍有存活(权限不足等) 则保留 PID 文件,让后续 Worker 重启的接管扫描 RecoverDaemonInstances 仍能发现并再次兜底,杜绝孤儿 永久失联——此时工作目录可能残留,由调用方 removeInstanceWorkDir 的校验重试如实反映。
func (*Manager) RecoverDaemonInstances ¶
RecoverDaemonInstances 在 Worker 重启后扫描 PID 目录,恢复仍存活的 daemon wrapper 连接。 对每个 PID 文件:wrapper pid 存活且 socket 可达则 reconnect 并登记实例为 RUNNING; 否则删除 PID 文件与残留 socket(清理)。返回成功恢复的实例数。 参见 ADR-003: 平台重启后通过 PID 文件重新连接已有 daemon。
func (*Manager) ReleaseCircuitBreaker ¶ added in v0.23.0
ReleaseCircuitBreaker 显式解除某实例的崩溃熔断(人工确认,spec §2.2/§5): 清熔断锁 + 清崩溃窗口(给予一次干净的重启配额)+ 恢复自动重启 + 清健康故障标识。 返回解除前的原因;released=false 表示此前并未熔断(幂等)。
调用点:Worker 收到 CP 的人工启动指令(gRPC StartInstance)——「熔断期间人工启动不受阻」 且「移除熔断锁后恢复自动重启」(spec §4 验收 5)由同一次人工动作完成。
FR-459 终验 Low(L2 竞态):即便本次调用未观测到熔断记账(had==false,多为「熔断刚被并发的 巡检/另一次解除清掉」),也幂等补发一次 enable。否则那条「scan 已读到 broken 并补发 disable、 而解除方此处的 enable 先到、disable 后到」的交错留下的 wrapper 粘性禁用态将**再无恢复路径** (后续人工 Start 因 !had 提前返回,永不下发 enable)。真正的收敛由 sendDisarmReconciled 的 下发后复核保证;此处 enable 是第二道保险,且只在当前确未熔断时下发(避免与并发 breakCircuit 反向交错成「已熔断却被 enable」)。
func (*Manager) Restart ¶
Restart 优雅重启实例:运行中先发停止命令让游戏服正常关服(保存世界、释放 world/session.lock、 输出关服日志),等旧进程完全退出后再启动;已停止/崩溃则直接启动。整个过程持有同一实例生命周期锁。
此前用强杀(killLocked)+ 立即启动:daemon 模式强杀 wrapper 进程树时,Unix 上自成进程组的 Java 可能未被杀到而沦为孤儿、仍占 world 锁,新进程随即启动即撞 Paper `SessionLock$ExceptionWorldConflict` (真机复现);且强杀无关服日志、跳过世界保存。改走优雅停止——stopLocked 下发 stop 控制帧由 wrapper 优雅关服(自带超时强杀兜底、复用同一策略);startLocked 内 strategy.Start() 的 daemon.WaitForPriorExit 依 PID 文件等旧 wrapper/Java 全退出(best-effort 上限)再拉起新进程, reapWrapper 的 `d.wrapperCmd != cmd` 陈旧守卫保证旧 reaper 不误改新实例状态。
func (*Manager) RestartIfRunning ¶ added in v0.23.0
RestartIfRunning 是 FR-459 假死自愈的受锁入口:与 Restart 相同地优雅重启,但**仅当**实例在 持锁复核时仍处 `RUNNING` 才动作。
只接受 RUNNING(spec §5:自愈不得与人工操作抢状态):
- STOPPED/CRASHED 已被人工停掉或本就未运行 → 拒绝,绝不把「已被人工停止的实例」误启动;
- STARTING/STOPPING 是过渡态:假死判定的前提是「进程在跑且不响应」,而过渡态本身尚未定型, 此时重启会与人工 Start/Stop 抢生命周期(原实现允许过渡态,与 spec 意图相悖,FR-459 复审项 4)。
复用 stopLocked/startLocked 既有优雅路径,**不新增杀进程路径**(spec §1/§5)。
func (*Manager) SendCommand ¶
SendCommand 向实例发送命令(通过 stdin)。
func (*Manager) SetCrashHandler ¶
SetCrashHandler 设置进程非正常退出回调(FR-313)。// 各策略在崩溃判定路径(direct/docker waitLoop、daemon 经 wrapper 退出事件)捕获现场后 经此扇出;回调须快速返回(上报本身应异步),不得阻塞策略的崩溃处理。
func (*Manager) SetDockerConfig ¶
func (m *Manager) SetDockerConfig(uuid, image string, mappings []PortMapping, cpuLimit float64, memLimitMB, diskLimitMB int64)
SetDockerConfig 设置已登记实例的 docker 镜像、端口映射与资源限额(ADR-019 / FR-079)。 由 CP 在创建/重注册 docker 实例时下发,使镜像/端口/限额对下一次启动生效(值在 Start 时随 spec 定型)。 实例不存在则忽略(与 SetGracefulStopTimeout 容错风格一致,不阻塞启动路径)。
func (*Manager) SetForeignRuntimes ¶ added in v0.23.0
func (m *Manager) SetForeignRuntimes(byUUID map[string]ForeignRuntime)
SetForeignRuntimes 由孤儿扫描每轮写入「实例目录下外来活进程」观测(key=实例 UUID)。 传 nil/空表示本轮无漂移。扫描是该缓存的唯一写入方,故整体替换而非增量合并—— 已被接管/自然退出而消失的漂移必须随本轮快照一起清掉。
func (*Manager) SetGracefulStopTimeout ¶
SetGracefulStopTimeout 更新已登记实例的优雅停止超时(秒)。 供 CP 在「重新注册已存在实例」时刷新该值,使设置变更对下一次启动生效(值在 Start 时随 spec 定型)。 实例不存在则忽略(与 SetRCONConfig 的容错风格一致,但此处不报错以免阻塞启动路径)。
func (*Manager) SetInstanceStartHandler ¶ added in v0.23.0
SetInstanceStartHandler 设置「实例开始新一轮运行」回调(FR-471 缺陷修复)。
与 SetStateChangeHandler 的区别:后者是通用状态流转通知(含 STOPPING/CRASHED 等), 本回调只在**一次新的运行真正开始**时触发一次(startLocked 置 STARTING 之后),语义等同 「新纪元开始」。用于让终端环形缓冲等「跨运行共享」的状态按本次运行重置——否则上一轮输出 会残留并污染本轮崩溃快照的根因归类。
func (*Manager) SetLaunchConfig ¶
func (m *Manager) SetLaunchConfig(uuid, startCommand, jdkPath, jdkBinPath string, envVars map[string]string, autoRestart bool)
SetLaunchConfig 刷新已存在实例的启动配置,供 CP 幂等重注册时下发(FR-233)。 运行中的策略继续服务且仅标记过期;停止/崩溃态则立即丢弃旧策略,确保下一次启动采用新规格。
func (*Manager) SetMemGuard ¶
func (m *Manager) SetMemGuard(cfg MemGuardConfig)
SetMemGuard 注入内存闸配置(main 从 worker.yml 接线)。
func (*Manager) SetMemReader ¶
func (m *Manager) SetMemReader(r readSysMem)
SetMemReader 注入系统内存读数器(仅测试)。
func (*Manager) SetOrphanAuditHandler ¶ added in v0.23.0
func (m *Manager) SetOrphanAuditHandler(handler func(action, targetID, detail string, success bool, errMsg string))
SetOrphanAuditHandler 注入孤儿处置审计回调(FR-455/456)。 worker main 装配时调用一次;不调用则回退 slog。
func (*Manager) SetOutputHandler ¶
SetOutputHandler 设置进程输出回调。 输出会路由到此处(用于桥接 WebSocket 终端)。
func (*Manager) SetProbePort ¶ added in v0.22.0
SetProbePort 更新已登记实例的 ServerProbe /metrics 端口(FR-411 补口)。 供 CP 幂等重注册时刷新:导入/历史实例起初 probe_port=0,CP 部署探针前补分配端口后经 重注册下发,Worker 内存表随即生效——心跳下一拍即开始采集该实例指标,无需等待实例重启。 实例不存在则忽略(容错风格与 SetGracefulStopTimeout 一致)。
func (*Manager) SetQueryPort ¶ added in v0.23.0
SetQueryPort 更新已登记实例的 query.port,供 Query 直探(FR-446)。容错风格与 SetProbePort 一致。
func (*Manager) SetRCONConfig ¶
SetRCONConfig 设置实例的 RCON 配置。
func (*Manager) SetResourceLimits ¶ added in v0.23.0
SetResourceLimits 只刷新实例记账里的 CPU/内存限额,其余启动配置不动(N-3)。
与 SetDockerConfig 的区别:后者要求 CP 把镜像/端口映射一并重推(一次完整重注册), 而启动/重启请求随附的限额是**运行期配额收紧**这一个字段的变化,不该顺带重写镜像与端口。
为什么必须标记 strategyStale:启动策略在构造时就定型了限额(见 startLocked 的 CommandSpec), 若不置过期标记,startLocked 会**复用旧策略**——收紧值照样不生效,只是把缺陷挪了个位置。 置位后由既有路径处理:运行中仅标记(cgroup 限额无法热改,停止时随旧策略一并丢弃), 停止/崩溃态则在下次 startLocked 入口锁外 Close 旧策略并重建(复用一条已验证的释放路径, 避免在此处重复实现「锁外关闭」而引入新的资源泄漏)。
仅在**值真的变化**时置过期:startLocked 每次启动都会调用本函数,无条件置位会让停止/崩溃态 的实例每次启停都被迫重建策略(多一次多余的构造与资源释放),且掩盖「限额未变」这一事实。
func (*Manager) SetServerPort ¶ added in v0.23.0
SetServerPort 更新已登记实例的 server-port(MC 游戏端口),供 SLP 直探(FR-446)。 供 CP 幂等重注册时刷新:端口迁移/导入实例后经重注册下发,Worker 内存表随即生效, 心跳下一拍即按新端口直探(无需重启进程)。实例不存在则忽略(容错风格与 SetProbePort 一致)。
func (*Manager) SetStateChangeHandler ¶
func (m *Manager) SetStateChangeHandler(handler func(instanceUUID string, oldState, newState InstanceState))
SetStateChangeHandler 设置实例状态变更回调。 每次实例状态发生转换时调用,用于 StreamInstanceEvents 推送。
type MemGuardConfig ¶
type MemGuardConfig struct {
// ReserveMB 保留水位(MB);0 = 默认策略 max(512MB, 总内存 10%)。
ReserveMB int64
// Disabled 显式关闭守卫(应急逃生口,如误判阻塞关键启动时)。
Disabled bool
}
MemGuardConfig Worker 实时内存闸配置(FR-317,worker.yml memory_guard 段)。
type OrphanDisposePolicy ¶ added in v0.23.0
type OrphanDisposePolicy string
OrphanDisposePolicy 运行期孤儿处置策略(FR-456)。
const ( // OrphanPolicyWarn 只告警 + 落审计,不自动清理(默认;管理员据审计介入)。 OrphanPolicyWarn OrphanDisposePolicy = "warn" // OrphanPolicyAuto 按扫描结果自动清理孤儿(强杀进程树 / 删除容器)。 OrphanPolicyAuto OrphanDisposePolicy = "auto" )
func NormalizeOrphanDisposePolicy ¶ added in v0.23.0
func NormalizeOrphanDisposePolicy(raw string) OrphanDisposePolicy
NormalizeOrphanDisposePolicy 归一化策略字符串:空/未知一律回退 warn(安全默认)。
type OrphanFinding ¶ added in v0.23.0
type OrphanFinding struct {
Kind OrphanKind
// InstanceUUID 相关实例 UUID;direct 孤儿无法解析时为空(以 WorkDir 标识)。
InstanceUUID string
// WorkDir 孤儿的工作目录(direct 孤儿的主标识)。
WorkDir string
// PIDs 相关进程 PID(docker 残留为空,改用 ContainerName)。
PIDs []int
// ContainerName docker 残留的容器名(其余为空)。
ContainerName string
// ContainerRunning docker 残留容器是否仍处于 running(FR-456 F13:running 容器 auto 档更保守)。
ContainerRunning bool
// ManagedLabel docker 残留容器是否带本平台受管标签(归属复核证据,FR-456 N5)。
ManagedLabel bool
// LabelInstanceUUID docker 残留容器受管标签内记录的实例 UUID(与容器名交叉核对用)。
LabelInstanceUUID string
// Disposed 是否已按 auto 策略实际处置。
Disposed bool
// Detail 供审计的补充说明。
Detail string
}
OrphanFinding 一次扫描发现的一条孤儿。
type OrphanKind ¶ added in v0.23.0
type OrphanKind string
OrphanKind 孤儿分类。
const ( // OrphanKindWrapperGoneJavaAlive daemon wrapper 已死、其托管的 Java 仍活。 OrphanKindWrapperGoneJavaAlive OrphanKind = "wrapper_gone_java_alive" // OrphanKindDirect direct 实例的 Java 孤儿(内存表已丢失该实例)。 OrphanKindDirect OrphanKind = "direct_orphan" // OrphanKindDockerLeftover docker 容器残留(内存表未认作 RUNNING 却容器在跑/已退出)。 OrphanKindDockerLeftover OrphanKind = "docker_leftover" // OrphanKindForeignRuntime 已注册实例的工作目录下存在外来活进程(FR-471)。 // 与前三类不同,它不是孤儿(实例仍在内存表中),而是「平台记账 STOPPED、磁盘却在跑」的对齐线索: // 本相只观测并上报,处置须由人工确认后经 AdoptForeignRuntime 接管。 OrphanKindForeignRuntime OrphanKind = "foreign_runtime" )
type OrphanScanner ¶ added in v0.23.0
type OrphanScanner struct {
// contains filtered or unexported fields
}
OrphanScanner 运行期周期孤儿扫描器(FR-456)。
func NewOrphanScanner ¶ added in v0.23.0
func NewOrphanScanner(mgr *Manager, interval time.Duration, policy OrphanDisposePolicy) *OrphanScanner
NewOrphanScanner 构造运行期孤儿扫描器。interval<=0 时取默认 60s;policy 空取 warn。
func (*OrphanScanner) ScanOnce ¶ added in v0.23.0
func (s *OrphanScanner) ScanOnce() []OrphanFinding
ScanOnce 执行一轮孤儿扫描并落审计。返回本轮发现(供单测/观测)。 本轮耗时超过 maxScanRoundDuration 时告警(FR-456 F14:扫描成本可观测)。
FR-471:第四相 scanForeignInRegisteredWorkdirs 与前三相共用同一轮进程枚举快照 (procs 只枚举一次),观测「已注册实例目录下的外来活进程」并写入 Manager 缓存供心跳上报。
func (*OrphanScanner) Start ¶ added in v0.23.0
func (s *OrphanScanner) Start(ctx context.Context)
Start 启动周期扫描 goroutine(随 ctx 取消而退出)。interval<=0 或 mgr 为空时不启动。
type PortMapping ¶
type PortMapping struct {
// ContainerPort 容器内监听端口(由镜像约定,如 MC 容器内 25565)。
ContainerPort int
// HostPort 宿主发布端口(来自 FR-032 端口池分配)。
HostPort int
// Protocol 端口协议,tcp(默认)或 udp(如 MC query)。
Protocol string
}
PortMapping 描述一条容器端口到宿主端口的发布关系(docker 模式,ADR-019)。
type PreflightCheckResult ¶
PreflightCheckResult 单项启动预检结果(FR-314)。
type ProcessType ¶
type ProcessType string
ProcessType 启动方式,与 model.ProcessType 对齐。 这里独立定义为字符串以避免 process 包反向依赖 controlplane/model。
const ( ProcessTypeDirect ProcessType = "direct" ProcessTypeDaemon ProcessType = "daemon" ProcessTypeDocker ProcessType = "docker" ProcessTypeRCON ProcessType = "rcon" )