diskqueue仅适用于单机、低吞吐、可容忍少量消息丢失的场景,是channel与数据库间的中间态,非Kafka或Redis替代品;其优势在于落盘可靠且零依赖,劣势在于无并发消费者、无ACK、无事务。

直接说结论:用 diskqueue 这类基于文件的 Go 消息队列,只适合单机、低吞吐、重启后可容忍少量消息丢失的场景;它不是 Kafka 替代品,也不是 Redis 备选,而是 channel 和数据库之间的一个“中间态”——有落盘保障,但没事务、没副本、没消费确认。
为什么选 diskqueue 而不是 channel 或 Redis
当你的服务需要比内存更可靠的暂存能力,又不想引入 Redis 实例或运维 Kafka 时,diskqueue 才值得考虑。比如:本地日志聚合器要缓冲几秒再批量写磁盘;嵌入式设备上跑的采集服务需断网续传;CI 构建任务队列要求进程重启后不丢待处理任务。
- 比
channel强在:消息写入文件,进程崩溃或重启后仍可恢复读取 - 比
Redis轻在:零外部依赖,一个 Go 包就能跑,适合边缘/离线环境 - 但比两者都弱在:不支持并发多消费者(只能一个 reader)、无 ACK 机制、不保证 exactly-once
diskqueue.New() 参数必须对齐实际负载
diskqueue 初始化时那串数字参数不是随便填的,每个都直接影响 IO 行为和内存占用:
- 第3个参数(如
10)是每个文件分片大小(MB),设太小会导致频繁切文件、inode 耗尽;设太大则恢复慢(重启时要 mmap 整个文件) - 第4个参数(如
4)是最大并发写文件数,不是消费者数——它控制后台 flush goroutine 数量,通常设为 CPU 核心数 - 第5个参数(如
1<<10)是内存缓冲区大小(字节),不是总队列容量;它只缓存待刷盘数据,满后会阻塞写入 - 第6个参数(如
2500)是每次刷盘最小字节数,低于它就等超时;设太小会高频小写,拖慢性能;设太大则延迟升高
示例中 disk.New("test", "tmp", 10, 4, 1<<10, 2500, 2*time.Second, 1*time.Second, ...) 是平衡写入延迟与磁盘压力的常见配置,别照搬,得看你的消息平均体积和吞吐。
立即学习“go语言免费学习笔记(深入)”;
消费者必须自己处理 nil 和重连逻辑
diskqueue.ReadChan() 在底层文件读完或发生 IO 错误时会关闭 channel 并返回 nil,这不是“队列空了”,而是“出错了”。你不能只写 for msg := range dq.ReadChan() 就完事:
- 收到
nil后必须调dq.Close(),再用相同参数重建实例,否则后续读不到新消息 - 重建后要 sleep 几毫秒再重试,避免反复失败打满 CPU
- 没有内置重试或死信机制,业务层得自己判断
msg解析失败是否该跳过、记录错误日志、还是写入 backup 文件 - 注意
ReadChan()返回的是[]byte,不是结构体——序列化/反序列化完全由你负责,JSON 失败不会自动丢弃,会卡住整个 reader
文件路径和权限最容易被忽略
diskqueue 不创建父目录,也不检查写权限。如果传入 "tmp" 但当前用户对 tmp 目录无写入权,或者 tmp 是只读挂载,它不会 panic,而是静默失败——所有 Write() 调用返回 nil 错误,且 ReadChan() 永远不吐数据。
上线前务必验证:os.Stat() 确认路径存在且可写;os.OpenFile(..., os.O_CREATE|os.O_WRONLY, 0644) 尝试创建测试文件;别依赖 IDE 的工作目录,硬编码绝对路径或用 filepath.Abs() 解析。


















