Maison > développement back-end > Golang > le corps du texte

Analyser le client http de Go

藏色散人
Libérer: 2021-05-10 21:56:23
avant
4858 Les gens l'ont consulté

Ce qui suit est une introduction au client http de Go de la colonne tutoriel golang. J'espère que cela sera utile aux amis dans le besoin !

Client HTTP

Go encapsule le client HTTP Il est très pratique de demander des données distantes. Voyez comment la couche inférieure du code source est réalisée.

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)
Copier après la connexion
Processus de demande approximatif

1. Construisez l'objet de demande selon les conditions de la demande

2. , sera traité par client.do()
func (c *Client) do(req *Request) (retres *Response, reterr error)
Copier après la connexion

2.1 la demande est traitée par 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
Copier après la connexion

3.send function
func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
    resp, err = rt.RoundTrip(req) }
Copier après la connexion

4. La méthode RoundTrip de DefaultTransport est en fait la méthode RoundTrip de Transport
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}
Copier après la connexion

5. objet de connexion *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}
Copier après la connexion

5.1 Transport.queueForDial initie la connexion
func (t *Transport) queueForDial(w *wantConn) {
    go t.dialConnFor(w)}
Copier après la connexion
5.2 Initie la numérotation dialConnFor
func (t *Transport) dialConnFor(w *wantConn) {
    pc, err := t.dialConn(w.ctx, w.cm) //发起拨号,返回连接对象
    delivered := w.tryDeliver(pc, err)}
Copier après la connexion
5.3 Initier la numérotation
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}
Copier après la connexion
5.4 Lecture de la coroutine Bien qu'il s'agisse d'une boucle for, la réponse demandée est lue en une seule fois. Si elle n'est pas fermée, la coroutine sera divulguée.
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)
        }
    }
Copier après la connexion
5.4.1 pc.readResponse Obtenir une réponse
func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTrace) (resp *Response, err error) {
    for{
        resp, err = ReadResponse(pc.br, rc.req) //获取response
    }}
Copier après la connexion
5.4.2 ReadResponse Lire la réponse
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)}
Copier après la connexion
5.5 Écriture de la coroutine
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))            
    }}
Copier après la connexion

6. Utilisez l'objet de connexion *persistConn pour obtenir une réponse
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
        }
    }}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:learnku.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal