worker进程安全退出需用context.withcancel控制生命周期,各goroutine定期检查ctx.done()并完成当前任务后再退出,主goroutine通过sync.waitgroup.wait()等待全部worker结束;防panic须在每个worker内defer recover()捕获,对易错操作优先用err判断而非依赖recover。

Worker 进程如何安全退出而不丢任务
Go 的 os.Signal 和 context.Context 是配合退出的核心。关键不是监听 SIGTERM,而是确保所有正在处理的任务完成后再关闭——否则 http.Server.Shutdown 那套逻辑在 Worker 里不适用。
实操建议:
- 用
context.WithCancel创建顶层上下文,在收到信号后调用cancel() - 每个工作 goroutine 必须定期检查
ctx.Done(),并在退出前完成当前任务(不能直接 return) - 主 goroutine 调用
sync.WaitGroup.Wait()等待所有 worker 结束,再退出进程 - 避免在
defer里做耗时清理(比如写日志到网络服务),它可能阻塞 shutdown
如何防止 panic 导致整个 Worker 崩溃
Worker 进程一旦 panic 就退出,没有类似 HTTP server 的 recover 机制兜底。必须在每个可能出错的执行路径上主动捕获。
实操建议:
- 每个长期运行的 goroutine 启动时包裹
recover(),例如:go func() { defer func() { if r := recover(); r != nil { log.Printf("worker panic: %v", r) } }() // 实际工作逻辑 }() - 不要依赖全局
http.DefaultServeMux或第三方库的 panic 捕获——它们不覆盖你的 goroutine - 对
json.Unmarshal、database/sql查询结果扫描等易 panic 操作,加if err != nil判断比 rely on recover 更可靠
如何控制并发数并避免 goroutine 泄漏
用 for range 从 channel 拉任务却不设限,很容易 spawn 几千个 goroutine 把内存打满;而用固定数量 worker 又容易因 channel 缓冲区设置不当导致阻塞或丢任务。
实操建议:
- 用带缓冲的
chan Task(如make(chan Task, 100)),缓冲区大小应略大于峰值每秒任务量 × 平均处理耗时 - 启动固定数量 worker(如
runtime.NumCPU()),每个 worker 循环select读取任务或响应 cancel - 向任务 channel 发送时用
select+default避免阻塞:如果 channel 满就丢弃或降级(如写入本地磁盘暂存) - 每次启动新 goroutine 前检查是否已收到 cancel —— 否则可能在 shutdown 过程中仍新建 goroutine
如何让 Worker 支持热重载配置而不中断任务
Worker 不像 Web server 那样有明确的请求生命周期,配置变更(如数据库地址、超时时间)必须平滑生效,不能简单 reload 进程。
实操建议:
- 把可变配置封装成 struct,并用
sync.RWMutex保护读写;更新时只写一次,避免频繁锁竞争 - 用
fsnotify监听配置文件变化,触发 reload 函数,但 reload 函数只更新内存配置,不重启 worker goroutine - 任务处理逻辑中每次使用配置前都用
RLock()读取,而不是初始化时拷贝一份静态值 - 避免在 reload 时修改 channel 容量或 worker 数量——这些属于结构性变更,必须重启进程才安全











