这篇文章给大家分享的是有关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的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。