原有实现中,WebSocket 状态推送散落在 webapi/api.go、webapi/task.go、batchmanage/manager.go 中,
通过手动调用 httpserver.Broadcast* 完成。这导致:
asaserver/server.go内部 goroutine 写入的中间态(start_initialization、starting、 失败后的stopped等)无法到达前端。- 前端收到的状态滞后甚至丢失(如启动重试全部失败后的
stopped状态)。 - 预检使用非原子
IsOperationAllowed,CAS 在 goroutine 内延迟执行,客户端无法立即得到拒绝响应。 ErrOperationNotAllowed时错误推送了操作失败事件(操作根本未执行)。batchmanage与 state dispatcher 存在重复广播,前端可能收到同一事件两次。
asaserver/state_manager.gowebapi/state_dispatcher.go(新建)webapi/actions.godocs/state-change-ws-push.md(架构说明)
在 StateManager 上增加轻量 pub/sub 机制。每次 WriteInstanceState 调用后,
writeStateLocked 除通知内部等待者(stateChange.Broadcast)外,
还向所有订阅 channel 非阻塞投递 InstanceState。
webapi 启动时订阅,将所有状态变更统一转发为 WebSocket 事件。
StateManager 新增字段:
subMu sync.Mutex
subs map[int]chan InstanceState
nextSubID int新增方法:
| 方法 | 说明 |
|---|---|
Subscribe(bufSize int) (int, <-chan InstanceState) |
注册订阅,返回 ID 和只读 channel |
Unsubscribe(id int) |
注销订阅,关闭 channel |
notifySubscribers(state InstanceState) |
在 sm.mu 持有期间非阻塞投递(锁序 sm.mu → subMu) |
writeStateLocked 在 stateChange.Broadcast() 后调用 notifySubscribers。
Close() 关闭所有订阅 channel(使订阅 goroutine 正常退出)。
包级便捷函数:SubscribeStateChanges(bufSize) / UnsubscribeStateChanges(id)。
状态 → WS 事件映射:
| InstanceStatus | event_type | status |
|---|---|---|
start_initialization / start_initialization_successful / starting |
server_starting |
starting |
started |
server_started |
started |
stopping |
server_stopping |
stopping |
stopped |
server_stopped |
stopped |
start_failed |
server_start_failed |
failed |
stop_failed |
server_stop_failed |
failed |
restart_failed |
server_restart_failed |
failed |
restarting |
server_restarting |
restarting |
restarted |
server_restarted |
restarted |
所有事件附带 data: {"raw_status": "...", "error": "...(可选)"}。
Start() 中 InitStateManager 成功后立即启动 dispatcher:
s.startStateChangeDispatcher(s.serverCtx)dispatcher goroutine 退出路径:
serverCtx.Done()(正常关闭)CloseStateManager()关闭所有订阅 channel
asaserver/server.gowebapi/api.gowebapi/task.go
将 CAS 提前到 HTTP handler 同步执行,立即返回 409(不允许)或 200(已预设状态)。
goroutine 通过 WithStatePreset() 跳过重复 CAS。
StartServerOptions 新增字段:
StatePreset bool // CAS 已由调用方完成,跳过内部 CAS新增 option 函数:
func WithStatePreset() StartServerOptionsFuncStartServer、StopServer(新增 ...StartServerOptionsFunc 参数)、RestartServer
均在 !opts.StatePreset 条件下才执行内部 CAS。
| Handler | 旧方式 | 新方式 |
|---|---|---|
startServer |
IsServerRunning + checkInstanceState |
CompareAndSwapInstanceState(stopped/failed → start_initialization) |
stopServer |
checkInstanceState |
CompareAndSwapInstanceState(started → stopping) |
restartServer |
checkInstanceState |
CompareAndSwapInstanceState(started → restarting) |
CAS 失败立即返回 HTTP 409;成功则 spawn goroutine。
删除 checkInstanceState 辅助函数及 httpserver import(已无用)。
三个 task 函数均传 WithStatePreset(),移除所有 ErrOperationNotAllowed 分支及对应错误广播:
| 函数 | 关键变化 |
|---|---|
runStartServerTask |
传 WithStatePreset(),移除 ErrOperationNotAllowed 分支 |
runStopServerTask |
传 WithStatePreset(),移除 ErrOperationNotAllowed 分支 |
runRestartServerTask |
传 WithStatePreset() + WithRestartStartupCompletion(func(string){}) 确保写入 StatusRestarted 状态 |
WithRestartStartupCompletion 空回调的作用:仅触发 StatusRestarted 状态写入,
使 dispatcher 能推送 server_restarted 事件,前端可区分重启完成与普通启动完成。
保留的广播(不经过 StateManager,dispatcher 感知不到):
ErrOperationNotAllowed路径(CAS 未发生,无状态变更)BroadcastUpdateStarted/Completed/Cancelled(更新任务事件)BroadcastBatch*(批量操作进度事件)
batchmanage/manager.go
与单实例操作对齐:替换非原子 IsOperationAllowed 为原子 batchDoCAS,
传 WithStatePreset() 给操作函数,移除所有与 state dispatcher 重复的 WS 广播。
新增 batchDoCAS 辅助函数:
func batchDoCAS(instanceName string, opType BatchOperationType) (bool, error)按操作类型调用对应的 CompareAndSwapInstanceState:
BatchStart:stopped/failed/... → start_initializationBatchStop:started → stoppingBatchRestart:started → restarting
runBatchOperation 改动:
将 IsOperationAllowed(非原子只读)替换为 batchDoCAS(原子 CAS):
- CAS 失败(状态不允许)→
InstanceSkipped - CAS 出错 →
InstanceFailed,failed++ - CAS 成功 → 调用
executeInstance(状态已预设)
删除不再使用的 toASAOperationType 辅助函数。
executeInstance 改动:
| 项目 | 旧 | 新 |
|---|---|---|
StartServer |
StartServer(instanceName) |
StartServer(instanceName, WithStatePreset()) |
StopServer |
StopServer(instanceName) |
StopServer(instanceName, WithStatePreset()) |
RestartServer |
RestartServer(instanceName, WithRestartStartupCompletion(广播回调)) |
RestartServer(instanceName, WithStatePreset(), WithRestartStartupCompletion(func(string){})) |
| 启动/停止/重启前广播 | BroadcastServerStartingEvent 等 |
删除(dispatcher 推) |
| 成功广播 | BroadcastServerStartedEvent 等 |
删除(dispatcher 推) |
| 失败广播 | BroadcastServerEvent("server_start_failed", ...) 等 |
删除(dispatcher 从 StatusStartFailed 等推) |
保留:批量操作自身的进度广播(BroadcastBatchOperationStarted/Progress/Completed),
这些事件与实例状态无关,不经过 StateManager。
| 事件类型 | 推送方 |
|---|---|
| 实例状态变更(starting/started/stopping/stopped/failed/restarting/restarted) | state dispatcher(统一) |
操作被拒绝(ErrOperationNotAllowed,仅单实例) |
task.go 手动广播 |
| 批量操作进度(batch_started/batch_progress/batch_completed) | batchmanage 手动广播 |
| 服务器更新进度(update_started/update_completed/update_cancelled) | task.go 手动广播 |