다음 튜토리얼 칼럼인 golang에서는 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)요청의 일반적인 프로세스
1. 요청 조건에 따라 요청 개체를 구성합니다
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}S 5.1 Transport.queueFordial은 DialConnFor
func (t *Transport) queueForDial(w *wantConn) { go t.dialConnFor(w)}E
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}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) } }
위 내용은 Go의 http 클라이언트 구문 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!