Maison > Article > développement back-end > Analyser le client http de Go
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 !
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)Processus de demande approximatif
func (c *Client) do(req *Request) (retres *Response, reterr error)
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
func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) { resp, err = rt.RoundTrip(req) }
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}
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}
func (t *Transport) queueForDial(w *wantConn) { go t.dialConnFor(w)}
func (t *Transport) dialConnFor(w *wantConn) { pc, err := t.dialConn(w.ctx, w.cm) //发起拨号,返回连接对象 delivered := w.tryDeliver(pc, err)}
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}
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) } }
func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTrace) (resp *Response, err error) { for{ resp, err = ReadResponse(pc.br, rc.req) //获取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)}
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)) }}
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 } }}
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!