linux写入文件失败时应区分错误类型:syscall.ebusy、eagain、eintr等可重试,os.errpermission、errnotexist等确定性错误不可重试;需用errors.is判断而非字符串匹配,重试须带随机抖动、响应ctx.done()并限制总耗时与次数。

写入文件失败时不该无差别重试,必须先判断错误类型——syscall.EBUSY、syscall.EAGAIN、syscall.EINTR 可重试;os.ErrPermission、os.ErrNotExist(父目录不存在)等确定性错误重试毫无意义。
哪些错误值得重试?用 errors.Is 判断,别字符串匹配
常见可恢复错误包括:
-
syscall.EBUSY:文件正被其他进程独占(Windows 常见)或设备忙 -
syscall.EAGAIN:资源暂不可用(如 NFS 缓存未就绪) -
syscall.EINTR:系统调用被信号中断,可立即重试,无需延迟 -
io.ErrUnexpectedEOF:读取固定结构时提前 EOF,可能因并发截断导致
必须用 errors.Is(err, syscall.EBUSY) 或类型断言,不能靠 err.Error() 包含 "busy" 字样来判断——不同系统错误信息不一致,且易被日志包装污染。
os.WriteFile 失败后怎么安全重试?
os.WriteFile 是原子写入,但失败后重试前需确认是否已部分落盘(极少见),更关键的是避免覆盖正在被读的旧文件。实操建议:
- 首次失败后,若错误是
syscall.EBUSY或EAGAIN,等待 50ms 后重试,最多 3 次,总耗时 ≤200ms - 每次重试前
select等待ctx.Done(),防止上下文取消后卡住 - 不要在重试循环里复用同一
backoff.BackOff实例——不同文件路径状态独立,共享退避会干扰判断 - 若目标是日志轮转或配置热加载,
os.ErrNotExist有时可重试(等待守护进程创建),但必须加MaxElapsedTime限制,比如最多等 500ms
io.WriteString + file.Write 场景下重试要注意什么?
这类非原子写入更易出问题,重试逻辑必须和文件状态对齐:
- 写入前先
os.Stat检查文件大小,写入失败后再次Stat对比——若大小突变,说明被其他进程截断或覆盖,应放弃重试并报错 - 并发写同一文件时,重试前先尝试
syscall.Flock(file.Fd(), syscall.LOCK_EX|syscall.LOCK_NB),抢锁失败立即重试,不 sleep -
io.Copy过程中失败,只对syscall.EIO、syscall.EBADF等底层 I/O 错误重试,io.ErrClosedPipe这类明确关闭的错误不重试
最易被忽略的一点:重试不是加个 for 循环再 time.Sleep 就完事——它必须响应 ctx.Done()、区分瞬态与永久错误、且每次重试间隔要带随机抖动(±20%),否则多个 goroutine 同步重试会触发“重试风暴”,反而压垮本地磁盘或 NFS 服务。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











