0

0

解析Go的http client

藏色散人

藏色散人

发布时间:2021-05-10 11:37:12

|

5184人浏览过

|

来源于learnku

转载

下面由golang教程栏目给大家介绍关于go的http client,希望对需要的朋友有所帮助!

http client

go封装了http客户端,请求远程数据非常方便,看些源码底层如何实现。

resp, err := http.Get("https://baidu.com") if err != nil {
    fmt.Printf("发起请求失败:%v", err)
    return }defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)

请求的大致流程

炉米Lumi
炉米Lumi

字节跳动推出的AI模型分享社区和模型训练平台

下载

1.根据请求条件,构建request对象

2.所有的client请求,都会经过client.do()处理

func (c *Client) do(req *Request) (retres *Response, reterr error)
2.1 request请求使用client.send()处理
func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error)resp, didTimeout, err = send(req, c.transport(), deadline)//默认传DefaultTransport

3.send函数

func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
    resp, err = rt.RoundTrip(req) }

4.DefaultTransport的RoundTrip方法,实际就是Transport的RoundTrip方法

func (t *Transport) roundTrip(req *Request) (*Response, error) {
    treq := &transportRequest{Request: req, trace: trace} //封装新的request
    cm, err := t.connectMethodForRequest(treq)
    pconn, err := t.getConn(treq, cm) //使用连接池技术,获取连接对象*persistConn,
    resp, err = pconn.roundTrip(treq) //使用连接对象获取response}

5.使用连接池技术,获取连接对象*persistConn

func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error) {
    w := &wantConn{ //构建连接对象
        cm:         cm,
        key:        cm.key(),
        ctx:        ctx,
        ready:      make(chan struct{}, 1),
        beforeDial: testHookPrePendingDial,
        afterDial:  testHookPostPendingDial,
    }
    if delivered := t.queueForIdleConn(w); delivered {//从连接池获取符合的连接对象,有就返回
        pc := w.pc        
        return pc, nil
    }    
    t.queueForDial(w)//发起连接

    select {
    case <-w.ready:    //连接准备好,就返回连接对象    
        return w.pc, w.err}
5.1 Transport.queueForDial发起连接
func (t *Transport) queueForDial(w *wantConn) {
    go t.dialConnFor(w)}
5.2 发起拨号dialConnFor
func (t *Transport) dialConnFor(w *wantConn) {
    pc, err := t.dialConn(w.ctx, w.cm) //发起拨号,返回连接对象
    delivered := w.tryDeliver(pc, err)}
5.3 发起拨号
func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *persistConn, err error) {
    pconn = &persistConn{ //构建连接对象
        t:             t,
        cacheKey:      cm.key(),
        reqch:         make(chan requestAndChan, 1),
        writech:       make(chan writeRequest, 1),
        closech:       make(chan struct{}),
        writeErrCh:    make(chan error, 1),
        writeLoopDone: make(chan struct{}),
    }
    conn, err := t.dial(ctx, "tcp", cm.addr()) //tcp连接,获取到net.conn对象

    pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize())//可以从conn读
    pconn.bw = bufio.NewWriterSize(persistConnWriter{pconn}, t.writeBufferSize())//写到conn

    go pconn.readLoop()//开启读协程
    go pconn.writeLoop()//开启写协程
    return pconn, nil}
5.4读协程,虽然是for循环,但是一次性就把请求的response读完了,如果没有关闭,就会造成协程泄露了
func (pc *persistConn) readLoop() {
    alive := true
    for alive {
        rc := <-pc.reqch //读取request,写入的地方在步骤6

        resp, err = pc.readResponse(rc, trace) //返回response
        //response的body是否可写,服务器code101才可写,所以正常这个是false
        bodyWritable := resp.bodyIsWritable()

        //response.Close设置循环结束,退出协程
        if resp.Close || rc.req.Close || resp.StatusCode <= 199 || bodyWritable {                    alive = false
        }          

        //把response写入通道,在步骤6会读取这个通道
        select {
        case rc.ch <- responseAndError{res: resp}:
        case <-rc.callerGone:
            return
        }
        //循环结束的一些情况
        select {
        case bodyEOF := <-waitForBodyRead: //读完body也会自动结束            
        case <-rc.req.Cancel:
        case <-rc.req.Context().Done():
        case <-pc.closech:
            alive = false
            pc.t.CancelRequest(rc.req)
        }
    }
5.4.1 pc.readResponse 获取response
func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTrace) (resp *Response, err error) {
    for{
        resp, err = ReadResponse(pc.br, rc.req) //获取response
    }}
5.4.2 ReadResponse读取response
func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
    tp := textproto.NewReader(r) //可以处理HTTP, NNTP, SMTP协议的内容,方便读取
    resp := &Response{
        Request: req,
    }    
    line, err := tp.ReadLine()//读取第一行,获取协议,状态码
    resp.Proto = line[:i]
    resp.Status = strings.TrimLeft(line[i+1:], " ")

    mimeHeader, err := tp.ReadMIMEHeader()//读取header头
    resp.Header = Header(mimeHeader)}
5.5 写协程
func (pc *persistConn) writeLoop() {
    for {
        select {
        case wr := <-pc.writech:
            startBytesWritten := pc.nwrite
            err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh))            
    }}

6.使用连接对象*persistConn获取response

func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
    var continueCh chan struct{}
    resc := make(chan responseAndError) //response通道

    pc.writech <- writeRequest{req, writeErrCh, continueCh}//written by roundTrip; read by writeLoop   

    pc.reqch <- requestAndChan{ //written by roundTrip; read by readLoop
        req:        req.Request,
        ch:         resc,
        addedGzip:  requestedGzip,
        continueCh: continueCh,
        callerGone: gone,
    }
    for { //监听这些通道
        testHookWaitResLoop()
        select {
        case err := <-writeErrCh:            
        case <-pc.closech:            
        case re := <-resc: //监听 response通道,返回response         
            return re.res, nil
        }
    }}

相关专题

更多
微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

2

2026.01.18

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

74

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

133

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

54

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

19

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

106

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

44

2026.01.15

ps图片相关教程汇总
ps图片相关教程汇总

本专题整合了ps图片设置相关教程合集,阅读专题下面的文章了解更多详细内容。

11

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.9万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号