json.Decoder 卡住是因为它等待完整 JSON 值,不自动处理流式分块或未闭合结构;需确保输入语义清晰(如 NDJSON)或明确边界。

为什么 json.Decoder 读不完就卡住?
常见现象是:用 json.Decoder 读取 HTTP 响应或管道数据时,程序 hang 在 Decode() 调用上,不报错也不返回。根本原因不是 bug,而是它默认等待完整 JSON 值——哪怕你只传了半个对象、多了一个逗号、或者流还没结束。
- HTTP 场景下,服务端可能分块传输(chunked),但
Decoder不会主动等下一块;它只从当前io.Reader里“啃”出一个合法 JSON 值就停,剩下的字节留在缓冲区 - 如果上游写入未关闭连接、也未发送完整 JSON(比如数组少了个
]),Decode()就一直阻塞 - 解决办法不是加超时(那只是掩盖问题),而是确保输入流语义清晰:用换行分隔的 JSON(NDJSON)或明确控制边界
json.Encoder 写完不刷缓存?
json.Encoder 内部带缓冲,调用 Encode() 后数据未必立刻写出。在流式场景(如写入 http.ResponseWriter 或 os.Stdout)中,这会导致接收方迟迟收不到数据,甚至超时断连。
- 必须显式调用
Flush()—— 它不是可选优化,是流式输出的必要步骤 - 注意:
Flush()只刷编码器自己的缓冲,不保证底层 writer(如bufio.Writer)也刷出去;如果 wrapper 了bufio.Writer,得再调一次它的Flush() - 示例:向 WebSocket 发送 JSON 流时,漏掉
enc.Flush(),前端永远收不到第一条消息
处理 NDJSON(每行一个 JSON)该用哪个 API?
日志、事件流、API 分页响应常用 NDJSON 格式。Go 标准库没有内置 NDJSON 解析器,但 json.Decoder 可以复用,只需注意换行处理。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 别用
bytes.Split()先切行再解析——浪费内存,且无法处理跨行字符串(JSON 字符串里允许换行) - 正确做法:把输入包装成
bufio.Scanner,用ScanLines拆行,每行丢给独立的json.NewDecoder(strings.NewReader(line))解析 - 性能提示:重复创建
Decoder开销小,比维护一个 Decoder 并手动跳过换行更可靠;因为Decoder不会自动跳过行尾符,自己管理状态容易出错 - 错误处理要细粒度:某一行解析失败(
invalid character),不影响后续行
Decoder/Encoder 能不能并发安全?
不能。同一个 json.Decoder 或 json.Encoder 实例被多个 goroutine 同时调用 Decode() 或 Encode(),会引发 panic 或数据错乱。
- 典型误用:起一堆 goroutine 共享一个
Decoder读同一 TCP 连接 —— 即使加锁也难保字节边界对齐,很可能把一个 JSON 值拆到两个 goroutine 里解析 - 正确模式:每个 goroutine 持有自己独立的
Decoder/Encoder,共享底层io.Reader/io.Writer时需额外同步(如用sync.Mutex包裹Read/Write) - 更推荐解耦:用 channel 分发解析后的结构体,而不是分发原始字节流
流式 JSON 最容易被忽略的其实是输入源的终止条件——Decoder 不会告诉你“后面没数据了”,它只认 JSON 语法完整性。所以别依赖 EOF 判断流结束,得靠业务层协议(比如约定长度头、特殊结束标记)或者超时兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










