這篇文章給大家分享的是有關Go中http client的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
go是golang的簡稱,golang 是Google開發的一種靜態強類型、編譯型、并發型,并具有垃圾回收功能的編程語言,其語法與 C語言相近,但并不包括如枚舉、異常處理、繼承、泛型、斷言、虛函數等功能。
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)請求的大致流程
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
}
}}感謝各位的閱讀!關于“Go中http client的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。