V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
zihuyishi
V2EX  ›  Go 编程语言

go 里面的 http response body 必须 close 是不是很不合理呀

  •  1
     
  •   zihuyishi · 307 天前 · 4327 次点击
    这是一个创建于 307 天前的主题,其中的信息可能已经有所发展或是发生改变。
    基本上每个新人写 http 请求都容易忘记这个,然后造成 goroutine 的泄漏。所以 http 请求基本都要实现一个 util 避免这种事情。
    然后就是 close 在 Response.Body 上而不是 Response 本身,直觉上 close response 更符合普遍的逻辑吧。虽然可以理解是因为 body 和 response 生命周期不一样,但是用起来还是感觉不太协调
    47 条回复    2024-02-04 11:15:09 +08:00
    BBCCBB
        1
    BBCCBB  
       307 天前
    response 和 body 是不同的资源吧, java 里 body 是 inputStream. 拿到后也是要 close 的. body close 后, response 依然可以用.
    body close 只代表你把响应里的 body 读取完了.. 但 response 里还有其他信息可以继续读的, cookie, header
    tyrantZhao
        2
    tyrantZhao  
       307 天前 via iPhone
    读一个 body 肯定要关,跟 http 没啥关系。
    mainjzb
        3
    mainjzb  
       307 天前   ❤️ 1
    老忘记的话,可以自己封装一层 http 请求。
    不这么设计的话,chunked 编码下容易遇到问题。
    777777
        4
    777777  
       307 天前
    有 timeout 自动会帮你关
    guanzhangzhang
        5
    guanzhangzhang  
       307 天前
    那用 head 请求呗
    mainjzb
        6
    mainjzb  
       307 天前
    楼主可能没有视频开发的经验
    以 b 站直播为例,打开 F12 可以发现 bilivideo.com 的请求在一直拉数据。如果客户端发生错误,可以调用 body close()主动关闭请求。例如检测到长时间无人操作关闭连接节约流量。 或者检测到浏览器缩小化,关闭画面,只拉声音,降低服务器负载。
    Mohanson
        7
    Mohanson  
       307 天前
    哈哈, 看了这个帖子发现前几天写的一段代码忘记关闭 Body 了
    AoEiuV020JP
        8
    AoEiuV020JP  
       307 天前
    高级一点的现代语言都有封装一些方便的方法,比如直接把 body 完整读取转成字符串并 close ,但 go 给人感觉就是现代语言却要对标上古 c/c++,这种细节小麻烦在 c/c++就是理所当然没什么不合理,
    flyv2x
        9
    flyv2x  
       307 天前
    确实因为没有 close 的问题,压测发现 bug 找了一下午,火大
    zihuyishi
        10
    zihuyishi  
    OP
       307 天前
    @tyrantZhao go 的问题在于你不读这个 body 也要 close ,然后很多时候不读就忘记了 close 。所以普通的请求都要自己套一层实现
    zihuyishi
        11
    zihuyishi  
    OP
       307 天前
    @777777 golang 里不会关吧,而且他这个 body 你不读他也要关。他底层还关系到了很多 goroutine,所以经常 pprof 跑一下发现一堆 goroutine 在那里持续了几天
    lvlongxiang199
        12
    lvlongxiang199  
       307 天前
    @AoEiuV020JP 如果是个文件呢 ? 直接读到内存里, 没准会 OOM
    DefoliationM
        13
    DefoliationM  
       307 天前
    不然自动关了全读内存里存着?不合理呀。
    mainjzb
        14
    mainjzb  
       307 天前
    理论上确实应该设计成 close response ,和文件 api 统一风格更合理。
    但是短期来看没希望了。response 已经有一个 Colse 变量了。无法再创建 Close 方法。
    sujin190
        15
    sujin190  
       307 天前 via Android
    body 是流式传输吧,有最大缓冲区,超过了就不会继续下载读取
    GooMS
        16
    GooMS  
       307 天前 via Android
    我觉得要关闭更好,好控制
    seers
        17
    seers  
       307 天前
    已经习惯性 defer 了,还好
    jinliming2
        18
    jinliming2  
       307 天前 via iPhone
    @AoEiuV020JP #8 能流式操作的不建议完整读取,还转字符串。性能会差,内存会炸。
    除非预先知道 body 大小足够小,或者是无法流式处理操作。
    jim9606
        19
    jim9606  
       307 天前 via Android
    go 没有 using 语法糖,所以还是 defer 吧,我看 open 之后马上 defer close 还挺常见的。
    flyqie
        20
    flyqie  
       307 天前 via Android
    其实,主要是 go 不太甜的原因。。
    pluto1
        21
    pluto1  
       306 天前 via iPhone
    作为一个官方库肯定是要考虑到所有场景的,就像上面说的视频,或者还有很多需要不断读取的场景。
    至于 Close 哪个的话这个就另说了,我个人感觉 Close body 好像合理一点。
    顺便提醒下楼上的,defer 前一定要检查 body 是不是 nil ,不然如果 body 出现 nil 的情况,defer 执行的时候我记得是会 panic 的
    lvlongxiang199
        22
    lvlongxiang199  
       306 天前
    我觉得很合理. `resp.Body` 就是个 reader 跟 fileReader 类似, 区别就是这是别人帮你 open 然后把 ownership move 给你, 负责 close 是用户的事, 你觉得 fileReader 必须 close 是不是合理的 ?
    虽然对于大部分调用 rest api 的情况, 是把 reader 内的读出来, 之后 close. 但是对于需要流式获取的场景并不合适(比如: 实时视频流, 大文件), 按我的理解, 进入标准库的东西, 得适配普遍的场景, 而非 rest api 这种单一场景
    crackidz
        23
    crackidz  
       306 天前
    语法糖也是要关闭的,只是不需要你手工关闭而已。response 对象的话包含了很多的其他内容,即使允许 close response 也能读取其中的其他信息,这其实很反直觉的。
    holulu
        24
    holulu  
       306 天前
    觉得不合理就换个语言吧,没必要纠结这些问题,不同的语言设计就不一样,要么自己设计一种符合自己习惯的。
    kingofzihua
        25
    kingofzihua  
       306 天前
    用 CI 工具,golangci-lint 支持检测 bodyclose
    CodeCodeStudy
        26
    CodeCodeStudy  
       306 天前
    所以就要用到框架啊,让框架来处理这些底层的、琐碎的事情
    maigebaoer
        27
    maigebaoer  
       306 天前 via Android
    我也遇到过这个坑😅😅😅
    gamexg
        28
    gamexg  
       306 天前
    @pluto1 #21

    看文档,至少官方实现 Body 不会出现 nil 的情况.
    我一直没检查过 body 是否为 nil

    Body represents the response body.
    The response body is streamed on demand as the Body field is read. If the network connection fails or the server terminates the response, Body.Read calls return an error.
    The http Client and Transport guarantee that Body is always non-nil, even on responses without a body or responses with a zero-length body. It is the caller's responsibility to close Body. The default HTTP client's Transport may not reuse HTTP/1.x "keep-alive" TCP connections if the Body is not read to completion and closed.
    6diyipi
        29
    6diyipi  
       306 天前
    文档就不看,出了问题逼逼赖赖。 官方库功能又不是为了满足你一个人, 想偷懒用 https://github.com/go-resty/resty
    gamexg
        30
    gamexg  
       306 天前
    我是习惯了,
    经常手写 net.Dial ,Close 是接着就会跟着的.

    其实我感觉应该是标准库为了提供足够的灵活性,
    按 golang 语言逻辑,如果不提供关闭,那么基本就只能内部实现 io.ReadAll ,那么意味着超大/超长延迟的响应会占用很大内存/很长时间.对于一些情况会有很大的影响.
    例如我读取一个很长的 api 响应,是直接对着 body 这个流进行解析,而不是全部读取到内存再处理.
    AoEiuV020JP
        31
    AoEiuV020JP  
       306 天前
    @jinliming2 #18 图的是方便,具体用不用肯定是需要开发者自己斟酌的,
    比如,上手学习测试, 请求 json api ,网页下载解析,都可能可以用,
    性能表现之类肯定比不过流处理,但高级语言服务场景也不是时时刻刻都要扣这一点点性能,所以我说这种细节坑在 c/c++没有不合理,但放在现代语言就显得不合群了,
    AoEiuV020JP
        32
    AoEiuV020JP  
       306 天前
    @lvlongxiang199 #12 没有哪个高级语言是不提供流读取的, 没有人会把二进制大文件转字符串读到内存里的,
    别给你个糖你就不吃饭了,饿死了怪别人给你糖,
    lvlongxiang199
        33
    lvlongxiang199  
       306 天前
    @AoEiuV020JP
    > 高级一点的现代语言都有封装一些方便的方法,比如直接把 body 完整读取转成字符串并 close
    我的意思是, 这种封装挪到第三方库就行了, 没必要放到标准库, 这东西的使用场景很有限. 流式读取的应用场景更广泛, []byte, string 都可以看作是流


    ps: **请你好好说话**
    lvlongxiang199
        34
    lvlongxiang199  
       306 天前
    @AoEiuV020JP 我仅仅说你提供的这个封装不适合处理这种情况, 没说 golang 没提供这种处理. io.Reader 就是一个流
    iseki
        35
    iseki  
       306 天前 via Android
    Go 标准库很多 API 设计得都比较有特色。抛开 Go 不谈对于 HTTP 库一个典型设计是 response / body 都有独立的 close ,response 的关闭会导致 body 被连带关闭。
    AoEiuV020JP
        36
    AoEiuV020JP  
       306 天前
    @lvlongxiang199 #33 我想说的就是你这种站在 c/c++视角想问题的,落后几十年了,
    要说不必须,那连 http 整个都不是必须的,都可以移到第三方库,你觉得是为什么 go 要自带 http 功能,
    现代语言提供必要功能之外的服务是很正常的事,毕竟现代每一个编程语言的出现都是为了解决已有语言的痛点,那就必定会提供不必要但是方便的东西,
    这方面 go 就感觉不上不下的,关键总有人把 go 拉到 c/c++这边把各种不方便合理化,
    我的观点是,go 如果哪天想开了加上这类不必要的方法也是理所当然的,
    AoEiuV020JP
        37
    AoEiuV020JP  
       306 天前
    @lvlongxiang199 #33 在我这里,反问=阴阳怪气=不好好说话,
    AoEiuV020JP
        38
    AoEiuV020JP  
       306 天前
    @lvlongxiang199 #34 我的意思就是糖不需要管饱,这种封装方法不需要解决所有情况,有就可以了,能用的时候可以提供方便就可以了, 不需要管不适用的情况,
    Nazz
        39
    Nazz  
       306 天前
    http 协议里面内容很多, 不要只考虑 json api
    xfriday
        40
    xfriday  
       306 天前
    GC 类语言没有析构导致的,GC 只能回收内存,其他资源只能手动销毁
    ihciah
        41
    ihciah  
       306 天前
    从设计上主要原因是 http 协议和客户端是两阶段读的。Response 读完 header 就可以返回,body 可以读或者不读。读不读也会影响连接管理,所以无论如何都需要一个显式关闭能力。
    至于为啥要手动关,那是语言本身问题,还是没析构函数/drop 的原因。
    thynson
        42
    thynson  
       306 天前
    没有 RAII 的语言可不是要手动关闭嘛。
    hotsymbol
        43
    hotsymbol  
       306 天前
    赶紧换 Rust ,Go 一堆历史遗留问题。只能平替 Python ,这些 web server 还是得交给 rust 和 Java
    lasuar
        44
    lasuar  
       305 天前
    go 的 net 库算是 low-level ,需要暴露这个方法。
    avrilko
        45
    avrilko  
       305 天前
    rust 适合你
    pluto1
        46
    pluto1  
       305 天前
    @gamexg 那应该是我记错了,我总记得我有次好像遇到过 Close 一个 nil 对象导致 panic 的,可能不是在 http 这边
    sampeng
        47
    sampeng  
       303 天前
    也没啥问题吧。。。新手?踩 1-2 次雷不就知道了。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1094 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:31 · PVG 03:31 · LAX 11:31 · JFK 14:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.