process

package
v0.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
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 / 健康位)。

View Source
const (
	// HealthProbeTCP 以 TCP connect 服务端口判响应(未部署探针的兜底维度)。
	HealthProbeTCP = "tcp"
	// HealthProbeHTTP 以 HTTP GET ProbePort 判响应(ServerProbe /metrics)。
	HealthProbeHTTP = "http"
)

响应维度探针类型(可配,spec §2.3 probeKind)。

View Source
const (
	// HealthActionWarn 仅告警 + 落审计 + 标原因(默认)。
	HealthActionWarn = "warn"
	// HealthActionRestart 走既有优雅重启路径自愈。
	HealthActionRestart = "restart"
	// HealthActionCircuitBreak 熔断(非可配动作,作为审计/上报动作标识)。
	HealthActionCircuitBreak = "circuit_break"
)

假死自愈动作(spec §2.2)。

View Source
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

View Source
var ErrNotImplemented = errors.New("该启动方式尚未实现")

ErrNotImplemented 表示该启动方式尚未实现。 docker/rcon 等未落地的策略统一返回此错误,避免误用。

Functions

func CheckDockerAvailable

func CheckDockerAvailable(ctx context.Context) (string, error)

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

func DefaultVerifyProcessOwnership(pid int, instanceUUID, workDir string, expectWrapper bool) bool

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

func PullDockerImage(ctx context.Context, ref string) error

PullDockerImage 从 registry 拉取镜像到 Worker 本机。 拉取流必须读尽,否则 Docker 守护进程可能中断拉取。

func RemoveDockerImage

func RemoveDockerImage(ctx context.Context, ref string, force bool) error

RemoveDockerImage 删除 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

type ImageSummary struct {
	ID        string
	Tags      []string
	SizeBytes int64
	Created   int64
}

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 NewManager

func NewManager(serversDir string) *Manager

NewManager 创建进程管理器。

func (*Manager) AdoptForeignRuntime added in v0.23.0

func (m *Manager) AdoptForeignRuntime(uuid string) (int, error)

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

func (m *Manager) GetInstance(uuid string) (*Instance, bool)

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

func (m *Manager) GetInstancePID(uuid string) int

GetInstancePID 获取实例进程的 PID。 策略未启动或已退出时返回 0。

func (*Manager) GetRCONConfig

func (m *Manager) GetRCONConfig(uuid string) (port int, password string, err error)

GetRCONConfig 获取实例的 RCON 配置。

func (*Manager) GetState

func (m *Manager) GetState(uuid string) (InstanceState, error)

GetState 获取实例状态。

func (*Manager) Kill

func (m *Manager) Kill(uuid string) error

Kill 强制终止实例。

func (*Manager) ListInstances

func (m *Manager) ListInstances() []string

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

func (m *Manager) ReapDaemonForDelete(uuid string)

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

func (m *Manager) RecoverDaemonInstances() (int, error)

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

func (m *Manager) ReleaseCircuitBreaker(uuid string) (reason string, released bool)

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) Remove

func (m *Manager) Remove(uuid string) error

Remove 移除实例记录。

func (*Manager) Restart

func (m *Manager) Restart(uuid string) error

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

func (m *Manager) RestartIfRunning(uuid string) error

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

func (m *Manager) SendCommand(uuid, command string) error

SendCommand 向实例发送命令(通过 stdin)。

func (*Manager) SetCrashHandler

func (m *Manager) SetCrashHandler(handler func(instanceID string, info CrashInfo))

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

func (m *Manager) SetGracefulStopTimeout(uuid string, seconds int)

SetGracefulStopTimeout 更新已登记实例的优雅停止超时(秒)。 供 CP 在「重新注册已存在实例」时刷新该值,使设置变更对下一次启动生效(值在 Start 时随 spec 定型)。 实例不存在则忽略(与 SetRCONConfig 的容错风格一致,但此处不报错以免阻塞启动路径)。

func (*Manager) SetInstanceStartHandler added in v0.23.0

func (m *Manager) SetInstanceStartHandler(handler func(instanceID string))

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

func (m *Manager) SetOutputHandler(handler func(instanceID string, stream string, data []byte))

SetOutputHandler 设置进程输出回调。 输出会路由到此处(用于桥接 WebSocket 终端)。

func (*Manager) SetProbePort added in v0.22.0

func (m *Manager) SetProbePort(uuid string, port int)

SetProbePort 更新已登记实例的 ServerProbe /metrics 端口(FR-411 补口)。 供 CP 幂等重注册时刷新:导入/历史实例起初 probe_port=0,CP 部署探针前补分配端口后经 重注册下发,Worker 内存表随即生效——心跳下一拍即开始采集该实例指标,无需等待实例重启。 实例不存在则忽略(容错风格与 SetGracefulStopTimeout 一致)。

func (*Manager) SetQueryPort added in v0.23.0

func (m *Manager) SetQueryPort(uuid string, port int)

SetQueryPort 更新已登记实例的 query.port,供 Query 直探(FR-446)。容错风格与 SetProbePort 一致。

func (*Manager) SetRCONConfig

func (m *Manager) SetRCONConfig(uuid string, port int, password string) error

SetRCONConfig 设置实例的 RCON 配置。

func (*Manager) SetResourceLimits added in v0.23.0

func (m *Manager) SetResourceLimits(uuid string, cpuLimit float64, memLimitMB int64)

SetResourceLimits 只刷新实例记账里的 CPU/内存限额,其余启动配置不动(N-3)。

与 SetDockerConfig 的区别:后者要求 CP 把镜像/端口映射一并重推(一次完整重注册), 而启动/重启请求随附的限额是**运行期配额收紧**这一个字段的变化,不该顺带重写镜像与端口。

为什么必须标记 strategyStale:启动策略在构造时就定型了限额(见 startLocked 的 CommandSpec), 若不置过期标记,startLocked 会**复用旧策略**——收紧值照样不生效,只是把缺陷挪了个位置。 置位后由既有路径处理:运行中仅标记(cgroup 限额无法热改,停止时随旧策略一并丢弃), 停止/崩溃态则在下次 startLocked 入口锁外 Close 旧策略并重建(复用一条已验证的释放路径, 避免在此处重复实现「锁外关闭」而引入新的资源泄漏)。

仅在**值真的变化**时置过期:startLocked 每次启动都会调用本函数,无条件置位会让停止/崩溃态 的实例每次启停都被迫重建策略(多一次多余的构造与资源释放),且掩盖「限额未变」这一事实。

func (*Manager) SetServerPort added in v0.23.0

func (m *Manager) SetServerPort(uuid string, port int)

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 推送。

func (*Manager) Start

func (m *Manager) Start(uuid string) error

Start 启动实例。按实例的 ProcessType 选择策略;首次启动时惰性构造策略。

func (*Manager) Stop

func (m *Manager) Stop(uuid string) error

Stop 停止实例。

func (*Manager) StopAll

func (m *Manager) StopAll()

StopAll 停止所有运行中的实例。 direct 模式:终止游戏服进程(Worker 退出时一并清理)。 daemon 模式:仅断开与 wrapper 的连接,wrapper 继续托管游戏服(ADR-003 进程隔离目标)。

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

type PreflightCheckResult struct {
	Name    string
	OK      bool
	Message string
}

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"
)

type ScannedProcess added in v0.23.0

type ScannedProcess struct {
	PID     int
	PPID    int // 父进程 PID(用于按进程树归属判定,如 preflight 排除实例自身进程树,FR-471)
	Cmdline string
	Cwd     string
}

ScannedProcess 进程枚举快照(供 direct 孤儿扫描;可由测试注入)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL