当前位置:网站首页>Go json.Decoder Considered Harmful
Go json.Decoder Considered Harmful
2022-07-28 09:59:00 【Fireflywang】
如果你在用 Go 语言编程,并且使用 json.Decoder 反解 JSON 负载,你可能会产生非预期效果。你应该使用 json.Unmarshal 代替 json.Decoder.
- json.Decoder 被设计用来反解 JSON 流,而非完整 JSON对象。
- json.Decoder 会忽略某些不合法的 JSON 语法。
- json.Decoder 没有释放网络连接用来重用(会导致拖慢 HTTP 请求到大约4倍时长)。
如果你度过了 json 包的文档,你不会诧异,确实如此。我已经搞错好多次了。大部分开发者发现使用 json.Decoder.Decode(...) 比使用 json.Unmarshal(...) 更方便解析 io.Reader 类型。
1. json.Decoder 为 JSON 流设计
JSON 流一般是串联的(concatenated)或以新行分割的 JSON 值。下面是一个例子:
{"Name":"Ed"}{"Name":"Sam"}{"Name":"Bob"}完整的流内容并不是一个合法的 JSON, 只有最外层用 [ ]包围时才是合法的 JSON 类型。
这只是串联的 JSON 对象,换句话说,它是合法的 JSON 流。
json.Decoder 类型被专门设计用以 JSON 流。最有可能的事,你的 JSON 负载并不适用于此。
那么 JSON 流为什么会存在?难道我们不能使用 JSON 数组?JSON 流主要用在:
- 在文件中存储结构化数据,并且在无需完全解析整个文件的情况下快速追加
- 从 API 等实时结构化流式数据(如
docker logs/docker eventsAPI等就是用此方法)
如果你是在解析单一完整的 JSON 对象,不要使用 json.Decoder。
2. json.Decoder 会忽略不合法语法
并非忽略掉所有不合法的语法,但是混合不合法和合法语法的 JSON 流会被 json.Decoder 忽略错误。 例如假设一个 API 返回:
{"Name": "Bob"}但是服务引入了 bug, 突然开始返回
{}{"Name": "Bob"}这明显是不合法的 JSON 负载,但是是一个合法的 JSON 流,json.Decoder 可以接受。
但是你不知道这种情况,你的代码会将这个返回反解为完整的 JSON 对象:
type Person struct {Name string}
...
var v Person
if err := dec.Decode(&v); err != nil {
panic(err)
}
fmt.Println(v.Name)你就会得到 v.Name 为空字符串,没有错误。json.Decoder 反解了第一个 JSON 对象, 剩余部分忽略掉了。
这可能发生吗?或许不会,但是你能 100% 确定吗?因为当发生的时候你不能容易 debug 出来。
3. json.Decoder 没有正确耗尽 HTTP 连接
这个问题最近由 Flippo Valsorda 提出来(link), 你可能会因此受到影响,除非你在使用 Go 1.7 (or above,译者注)。
如果你正在创造一个 HTTP 请求,传输返回体到 json.Decoder#Decode() (大部分人会这样做做)然后极有可能你的连接没有被正确耗尽,可能使你的 HTTP 客户端变慢4倍。
如果 HTTP 端点返回单一完整的 JSON 对象,并且你只调用 json.Decoder#Decode() 一次,这可能意味着你没有读取到 io.EOF 返回信号。因此你没有根据 io.EOF 终结 json.Decoder,返回体依然是开放中,TCP 连接(或者其他正在使用的 Transport)不能被返回到连接池中即使你已经读完了。 了解更多请点击URL。
现在在 golang 主分支master中已经修复了, 最可能将在 Go1.7 中发布。(这篇文章写于2016年4月28日,彼时尚未发布 Go1.7)
同时,如果你的返回体足够小,只需要用 ioutil.ReadAll 全部读入内存并且使用 json.Unmarshal 反解。如果你想继续使用 json.Decoder, 你需要耗尽返回体中未读完的部分,例如:
io.Copy(ioutil.Discard, resp.Body)因此,如果你正在使用 json.Decoder, 请检查你的代码,将所有 defer resp.Body.Close() 替换为:
defer func() {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
}结论
如果你没有处理 JSON 流,请不要使用 json.Decoder。
使用 json.Unmarshal:
- 如果你不知道 Go JSON 流是什么
- 如果你正在处理单一 JSON 对象
- 如果远程 API 有可能返回有问题的 JSON
现在你知道权衡策略了,那就自己决定吧。
参考资料
边栏推荐
猜你喜欢

Skillfully use NGX_ Lua makes traffic grouping

ADVANCE.AI出海指南助力企业出海印尼,掌握东南亚市场半边天

OSPF的LSA及优化
![[esp32][esp idf] ap+sta realizes wireless bridging and transferring WiFi signals](/img/bf/0a968064a8f7c11b86a2a2820208e6.png)
[esp32][esp idf] ap+sta realizes wireless bridging and transferring WiFi signals

5、动态规划---斐波那契数列

巧用ngx_lua做流量分组

6、双指针——递增数组两数之和与目标数相等

14、双指针——盛最多水的容器

二维前缀和

Thinking and summary of technical personnel | R & D Efficiency
随机推荐
ES(8.1)认证题目
Introduction to evaluatorfilter
02.1.2.逻辑类型 bool
我用小程序容器让移动研发效率提升了5倍!
广州地铁14号线新市墟站开建,白云区居民即将开启双线换乘模式!
记录一次idea中的父子项目修改project与module名称,亲测!
leetcode——旋转数组的最小数字
基于docker安装MySQL
_HUGE and __IMP__HUGE in “math.h“
Consul
数据库mysql基础
2022 uni app parsing token standard - use jsrsasign - climb the pit
技术人 | 研发效能的思考总结
定了!就在7月30日!
[esp32][esp idf] esp32s3 quickly build lvglv7.9
Arthas tutorial
Continue to write the greatest work based on modelarts [play with Huawei cloud]
Prometheus 运维工具 Promtool (四)TSDB 功能
IE兼容性问题处理
【JS高级】js之函数、重载、匿名函数、作用域及作用域链_03