golang的HTTP流式客户端实现方法与对比

本文介绍了在Go中处理HTTP响应,特别是Content-Type为json的情况,使用bufio.Reader、bufio.NewScanner、json.Decoder和httputil.NewChunkedReader等方法的优缺点。bufio.NewReader适合json/plain-text但可能引发粘包问题,bufio.NewScanner按分隔符解析适合单行内容,json.Decoder简洁但要求固定格式的Json,而httputil.NewChunkedReader适用于HTTP-Chunked协议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

golang 在接收流式数据时有多个方式,本文以 HTTP 协议下如何处理 Content-Type: json 下的响应为例,介绍几种流式协议处理方法。一般而言,我们主要通过 resp.Body 来接收响应的消息:

方法

bufio.Reader

func Read(resp *http.Response, out chan <- interface{}) error {
	r := resp.Body
    defer r.Close()
    br := bufio.NewReader(r)

    for {
        m := &Response{} // Response is the struct of response in this request
        buf := make([]byte, 1024)
        l, err := br.Read(buf)
        if err != nil && err != io.EOF {
            return err
        }
        if pe := json.Unmarsha(buf[:l], m); pe != nil {
            return pe
        }
        out <- m
        if err == io.EOF {
            return nil
        }
    }   
}

先通过 bufio.NewReader 建立缓冲队列,然后再从缓冲队列中消费,这种方式比较灵活,对 json/plain-text 都适用。但是会存在消息堆积的隐患,导致两个粘在一起的 json 包解析失败,适用于读取 plain-text ,且协议中响应的内容没有明确的分隔符。

bufio.NewScanner

func Read(resp *http.Response, out chan <- interface{}) error {
	r := resp.Body
    defer r.Close()
    sc := bufio.NewScanner(r)

    for {
        m := &Response{} // Response is the struct of response in this request
        if !sc.Scan() {
            return sc.Err()
        }
        s := sc.Text()
        if pe := json.Unmarsha(buf[:l], m); pe != nil {
            return pe
        }
        out <- m
    }
}

这个方法通过换行符对来到的消息进行分割解析(这里也可以自定义分割函数),这种方法就可以避免出现上述方法中因消费赶不上生产导致粘包出现的解析失败问题,但不太适合 plain-text,这样每次只会流出一行。

json.Decoder

func Read(resp *http.Response, out chan <- interface{}) error {
	r := resp.Body
    defer r.Close()
    dec := json.NewDecoder(r)
    for {
        m := &Response{} // Response is the struct of response in this request
        if err := dec.Decode(&m); err != nil {
            if err != io.EOF {
                return err
            }
            out <- m
            return nil
        }
        out <- m
    }  
}

直接使用 json.Decoder 对接收端的 io.ReadCloser 进行解码,需要明确知道 Response 的协议是什么,如果 Json 格式不确定,例如 OpenAI 中的 Chat 接口,则会导致不在预期内的 Json 解析失败。但优点在于简单明了,代码简单。

httputil.NewChunkedReader

func Read(resp *http.Response, out chan <- interface{}) error {
	r := resp.Body
    defer r.Close()
    cr := httputil.NewChunkedReader(r)
    for {
        m := &Response{} // Response is the struct of response in this request
        buf := make([]byte, 1024)
        l, err := cr.Read(buf)
        if err != nil && err != io.EOF {
            return err
        }
        if pe := json.Unmarsha(buf[:l], m); pe != nil {
            return pe
        }
        out <- m
        if err == io.EOF {
            return nil
        }
    }  
}

适用于标准的 HTTP-Chunk 协议,这要求响应的返回的分隔符为 \r\n ,然后依次给出消息的大小以及消息的响应体,适用条件比较苛刻。

总结

FreedomDisadvantageContent-Type
bufio.Reader🌟🌟🌟存在粘包风险plain-text
bufio.NewScanner🌟🌟消息按照分割符分开plain-text/json
json.Decoder🌟Body格式需要严格按照给定 json 返回json
httputil.NewChunkedReader🌟协议需要严格遵循 HTTP-Chunked 协议json
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值