您好,登錄后才能下訂單哦!
這篇文章主要講解了“golang的httpclient怎么發起http請求”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“golang的httpclient怎么發起http請求”吧!
一、net/http的httpclient發起http請求
方法
get請求
func httpGet() {
resp, err := http.Get("http://www.01happy.com/demo/accept.php?id=1")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
post請求
方法一:http.Post方法
func httpPost() {
resp, err := http.Post("http://www.01happy.com/demo/accept.php",
"application/x-www-form-urlencoded",
strings.NewReader("name=cjb"))
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
使用這個方法,第二個參數(contentType)必須設置為"application/x-www-form-urlencoded",否則post參數無法傳遞
方法二:http.PostForm方法
func httpPostForm() {
resp, err := http.PostForm("http://www.01happy.com/demo/accept.php",
url.Values{"key": {"Value"}, "id": {"123"}})
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
復雜的請求:若需要設置請求頭參數,cookie之類的數據,就使用http.Do方法
func httpDo() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://www.01happy.com/demo/accept.php", strings.NewReader("name=cjb"))
if err != nil {
// handle error
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "name=anny")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
Head請求:Head方法,只返回頁面的首部
注意:
要調用resp.Body.Close()關閉response.body。如果resp.body沒有關閉,則Client底層RoundTripper將無法重用存在的TCP連接去服務接下來的請求
第二步:Do/Get/Post方法的實現(以Do為例)
處理請求,添加referer、method字段
調用send方法,向request添加cookie
檢查http頭是否合法,若合法調用transport的RoundTrip方法
第三步:精髓:調用transport的RoundTrip方法
++transport.go:++
struct:
type Transport struct {
idleMu sync.Mutex
wantIdle bool // user has requested to close all idle conns
idleConn map[connectMethodKey][]*persistConn
idleConnCh map[connectMethodKey]chan *persistConn
reqMu sync.Mutex
reqCanceler map[*Request]func()
altMu sync.RWMutex
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
//Dial獲取一個tcp 連接,也就是net.Conn結構,你就記住可以往里面寫request
//然后從里面搞到response就行了
Dial func(network, addr string) (net.Conn, error)
}
兩個map:
idleConn:保存從 connectMethodKey (代表著不同的協議 不同的host,也就是不同的請求)到 persistConn 的映射
idleConnCh:用來在并發http請求的時候在多個 goroutine 里面相互發送持久連接,也就是說, 這些持久連接是可以重復利用的, 你的http請求用某個persistConn用完了,通過這個channel發送給其他http請求使用這個persistConn
==連接池:==
RoundTrip方法:
func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
...
pconn, err := t.getConn(req, cm)
if err != nil {
t.setReqCanceler(req, nil)
req.closeBody()
return nil, err
}
return pconn.roundTrip(treq)
}
省略前面對參數的檢查部分,主要有兩步:
第一步:獲取TCP長連接pconn, err := t.getConn(req, cm)
func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) {
...
type dialRes struct {
pc *persistConn
err error
}
dialc := make(chan dialRes)
//定義了一個發送 persistConn的channel
...
// 啟動了一個goroutine, 這個goroutine 獲取里面調用dialConn搞到
// persistConn, 然后發送到上面建立的channel dialc里面,
go func() {
pc, err := t.dialConn(cm)
dialc <- dialRes{pc, err}
}()
idleConnCh := t.getIdleConnCh(cm)
select {
case v := <-dialc:
// dialc 我們的 dial 方法先搞到通過 dialc通道發過來了
return v.pc, v.err
case pc := <-idleConnCh:
// 這里代表其他的http請求用完了歸還的persistConn通過idleConnCh這個
// channel發送來的
handlePendingDial()
return pc, nil
case <-req.Cancel:
handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
case <-cancelc:
handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
}
}
定義一個發送 persistConn的channel dialc
啟動了一個goroutine, 這個goroutine 獲取里面調用dialConn搞到persistConn, 然后發送到dialc里面
主協程goroutine在 select里面監聽多個channel,看看哪個通道里面先發過來 persistConn,就用哪個,然后return
第二步:調用這個持久連接persistConn 這個struct的roundTrip方法
三個goroutine通過channel互相協作的過程,
1. 主goroutine ->requestAndChan -> 讀循環goroutine:讀循環goroutine 通過channel requestAndChan 接受主goroutine發送的request(rc := <-pc.reqch), 并從tcp輸出流中讀取response, 然后反序列化到結構體中, 最后通過channel 返給主goroutine (rc.ch <- responseAndError{resp, err} )
2. 主goroutine ->writeRequest-> 寫循環goroutine:select channel中主gouroutine的request,然后寫入tcp輸入流,如果出錯了,channel 通知調用者
3. 主goroutine 通過select 監聽各個channel上的數據, 比如請求取消, timeout,長連接掛了,寫流出錯,讀流出錯, 都是其他goroutine 發送過來的, 跟中斷一樣,然后相應處理
二、使用net/http的參數設置:
粗粒度:
使用http.Client的 Timeout字段。
它的時間計算包括從連接(Dial)到讀完response body。
細粒度:細粒度只對于單次連接起作用
net.Dialer.Timeout 限制建立TCP連接的時間
http.Transport.TLSHandshakeTimeout 限制 TLS握手的時間
http.Transport.ResponseHeaderTimeout 限制讀取response header的時間
http.Transport.ExpectContinueTimeout 限制client在發送包含 Expect: 100-continue的header到收到繼續發送body的response之間的時間等待。
http.Transport.IdleConnTimeout,控制連接池中一個連接可以idle多長時間。
http.Client的默認超時時限是0,不超時,可以設置。
實際上是一個連接池,全局復用。初始化Transport,然后復用
參數:
DisableKeepAlives默認為false(長連接)
MaxIdleConns連接池對所有host的最大連接數量,默認無窮大
MaxIdleConnsPerHHost連接池對每個host的最大連接數量。(一個host的情況下最好與上面保持一致)
IdleConnTimeout空閑時間設置
函數:
DialContext用于創建(http)連接。
感謝各位的閱讀,以上就是“golang的httpclient怎么發起http請求”的內容了,經過本文的學習后,相信大家對golang的httpclient怎么發起http請求這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。