Go fastjson 怎样复用Parser减少临时对象
来源:17golang原创
时间:2026-10-31 11:04:21 385浏览 收藏
在循环或高频请求中反复解析 JSON 时,fastjson 的关键优化不是把所有状态做成全局变量,而是让同一个调用方复用局部 Parser,并在下一次 Parse 前结束旧 Value 的使用。并发场景再用 ParserPool 借出独占的 Parser,就能减少解析器本身的临时分配,同时保持请求之间的边界清楚。
官方地址:https://pkg.go.dev/github.com/valyala/fastjson
- 同一份 JSON 需要读取多个字段时,先
Parser.Parse一次,再对返回的Value调用多个Get*方法。 - Parser 返回的 Value 可能引用 Parser 的内部数据;下一次 Parse 前不要继续保存旧 Value。
- 并发请求不能共享一个可变 Parser;用
ParserPool借用、使用、归还,每次只让一个请求持有。
先判断:便捷函数还是一次 Parse 多次读取
fastjson.GetString、GetInt 等顶层函数适合只取一个字段的短代码。若同一份 JSON 要读取多个互不相邻的字段,继续逐个调用顶层函数会重复解析输入。此时应把输入交给 Parser 一次,再从同一个 Value 读取字段:
package main
import (
"fmt"
"log"
"github.com/valyala/fastjson"
)
func readPayload(data []byte) (string, int, error) {
var parser fastjson.Parser
// 一次解析整份 JSON,后续字段读取共享同一个 Value。
value, err := parser.ParseBytes(data)
if err != nil {
// 解析失败时立即返回,避免继续读取无效 Value。
return "", 0, err
}
name := value.GetStringBytes("user", "name")
count := value.GetInt("items", "count")
return string(name), count, nil
}
func main() {
name, count, err := readPayload([]byte(`{"user":{"name":"Lin"},"items":{"count":3}}`))
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s %d\n", name, count)
}
这里的优化点是“解析一次、读取多次”,而不是把 Parser 做成共享全局对象。Parser 是可变状态,局部变量最容易表达独占关系;需要并发处理时,再把这个模式放进池中。

复用局部 Parser 时,Value 的生命周期要收紧
官方文档提示,Parser 递归返回的部分对象不能跨过下一次 Parse 继续使用。实际代码中可以把“解析、读取、转换为业务值”放在同一个函数里,让 Value 不逃逸到下一轮循环:
func collectNames(inputs [][]byte) ([]string, error) {
var parser fastjson.Parser
names := make([]string, 0, len(inputs))
for _, data := range inputs {
// Parse 会更新 Parser 的内部状态,本轮只在这里使用 value。
value, err := parser.ParseBytes(data)
if err != nil {
return nil, err
}
// 转成独立的 Go string,再进入下一次 Parse。
names = append(names, value.GetStringBytes("user", "name")...)
}
return names, nil
}
这个例子刻意没有把 *fastjson.Value 保存到切片,也没有把它交给 goroutine。若需要把字符串或字节交给异步任务,应先确认返回数据是否仍依赖 Parser 的内部存储;不确定时,复制成独立的业务数据,再进入异步边界。
复用 Parser 的前提是“上一轮的 Value 已经完成消费”,不是“Parser 可以永久保存所有历史结果”。只要下一轮 Parse 可能覆盖内部状态,就把 Value 当作当前轮次的借用视图。
并发请求用 ParserPool,而不是共享一个 Parser
服务端通常有多个请求同时解析 JSON。一个全局 Parser 会让请求之间共享可变状态,既难以保证数据正确,也会制造竞态。ParserPool 的职责是提供可复用的 Parser;借出后由当前请求独占,处理完成后归还:
var parserPool fastjson.ParserPool
func parseRequest(data []byte) (int, error) {
parser := parserPool.Get()
defer parserPool.Put(parser)
// 当前请求独占 parser;返回前完成所有 Value 读取。
value, err := parser.ParseBytes(data)
if err != nil {
return 0, err
}
total := value.GetInt("items", "count")
return total, nil
}
defer 能覆盖成功和错误两条路径,但不能替你处理外部引用:如果把 value 或它返回的视图交给异步任务,函数返回后 Parser 可能已经被归还并再次使用。正确做法是复制需要跨越函数边界的数据,或者把消费动作放在归还之前。

哪些改造会让复用失去意义
| 写法 | 问题 | 更合适的边界 |
|---|---|---|
| 每个字段都调用顶层 Get* | 同一份输入被重复解析 | 一次 Parse 后读取多个字段 |
| 所有 goroutine 共用一个 Parser | 可变解析状态互相覆盖 | ParserPool 每次借出一个独占实例 |
| 保存 Value 到下一轮循环 | 下一次 Parse 后引用可能失效 | 当前轮次完成读取并转成独立数据 |
| 把 Value 的视图交给异步任务 | Parser 归还后底层数据可能变化 | 复制后再跨线程或队列传递 |
还要控制输入大小。官方说明指出,解析过程可能按输入规模占用内存,因此复用 Parser 不能替代请求体上限、超大 JSON 拒绝策略和错误处理。复用解决的是重复分配压力,不是无限放大输入容量。
落地前的判断清单
- 同一份 JSON 是否需要读取多个字段?如果只取一个字段,顶层便捷函数可能更直观。
- Parser 是否只被一个调用方持有?并发场景是否通过
ParserPool借用而不是共享? - 下一次
Parse前,旧 Value 和其视图是否已经全部消费? - 跨函数、goroutine 或队列传递的数据是否已经复制到独立存储?
- 是否设置了输入大小上限,并把解析错误作为正常分支处理?
常见问题
复用 Parser 会不会让所有结果都被覆盖?
只有仍依赖 Parser 内部状态的 Value 或视图才有生命周期约束。需要长期保存的字段应先转换或复制成独立的 Go 数据。
ParserPool 是否意味着可以把 Parser 放进全局并发使用?
不是。池可以是全局的,但每次 Get 到的 Parser 只能由当前调用方独占,Parse、读取和 Put 之间不能被多个 goroutine 同时操作。
复用 Parser 后还需要关注请求体大小吗?
需要。Parser 复用只减少反复创建解析器的分配,不能消除大输入本身带来的内存占用,应配合输入上限和解析错误处理。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
Golang · Go教程 | 37分钟前 | 标准库 · csv · Go教程 · csv Go error FLUSH CRLF encoding/csv Writer.UseCRLF Windows换行103 收藏
-
Golang · Go教程 | 39分钟前 | 标准库 · 错误处理 · IO · Go教程 · Go eof io.Pipe CloseWithError PipeReader PipeWriter.Close321 收藏
-
329 收藏
-
365 收藏
-
178 收藏
-
362 收藏
-
154 收藏
-
343 收藏
-
148 收藏
-
443 收藏
-
474 收藏
-
375 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习