Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)
一、HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)
HTTP協(xié)議是基于TCP/IP協(xié)議棧的,并且它也是一個(gè)面向普通文本的協(xié)議。
只要搞清楚了HTTP請(qǐng)求的報(bào)文(報(bào)文的頭部(header)和主體(body))應(yīng)該包含的內(nèi)容,使用任何一個(gè)文本編譯器,就餓可以編寫一個(gè)完整的HTTP請(qǐng)求報(bào)文。
在這種情況下,直接使用net.Dial
函數(shù),就可以。
使用net/http
代碼包中的程序?qū)嶓w,可以更便捷的訪問(wèn)基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)。其中最便捷的是使用http.Get
函數(shù)。
1.1 使用http.Get
函數(shù)訪問(wèn)HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)
package main import ( "fmt" "net/http" ) func main() { url1 := "http://www.google.cn/" fmt.Printf("Send request to %q with method GET ... \n", url1) response1, err := http.Get(url1) if err != nil { fmt.Printf("request sending error: %v\n", err) } defer response1.Body.Close() line1 := response1.Proto + " " + response1.Status fmt.Printf("The first line of response: \n %s \n", line1) }
http.Get
函數(shù)會(huì)返回兩個(gè)結(jié)果值:
- 第一個(gè)結(jié)果值的類型是*http.Response,它是網(wǎng)絡(luò)服務(wù)給我們傳回來(lái)的響應(yīng)內(nèi)容的結(jié)構(gòu)化表示。
- 第二個(gè)結(jié)果值是error類型。它代表了在創(chuàng)建和發(fā)送HTTP請(qǐng)求,以及接受和解析HTTP響應(yīng)的過(guò)程中可能發(fā)生的錯(cuò)誤。
在http.Get
函數(shù)內(nèi)部會(huì)使用缺省的HTTP客戶端,并調(diào)用它的Get方法以完成功能。缺省客戶端類型是*http.Client
,由公開變量DefaultClient代表。
1.2 使用缺省客戶端DefaultClient(類型為*http.Client
)
package main import ( "fmt" "net/http" ) func main() { url1 := "http://www.google.cn/" fmt.Printf("Send request to %q with method GET ... \n", url1) // response1, err := http.Get(url1) response1, err := http.DefaultClient.Get(url1) if err != nil { fmt.Printf("request sending error: %v\n", err) } defer response1.Body.Close() line1 := response1.Proto + " " + response1.Status fmt.Printf("The first line of response: \n %s \n", line1) }
它的基本類型(http.Client
)可以開箱即用。
1.3 使用http.Client
訪問(wèn)HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)
package main import ( "fmt" "net/http" ) func main() { url1 := "http://www.google.cn/" fmt.Printf("Send request to %q with method GET ... \n", url1) // response1, err := http.Get(url1) // response1, err := http.DefaultClient.Get(url1) var oneClient http.Client response1, err := oneClient.Get(url1) if err != nil { fmt.Printf("request sending error: %v\n", err) } defer response1.Body.Close() line1 := response1.Proto + " " + response1.Status fmt.Printf("The first line of response: \n %s \n", line1) }
http.Client
是一個(gè)結(jié)構(gòu)體類型,并且它包含的字段是公開的。之所以該類型的零值仍然可以使用,是因?yàn)樗倪@些字段要么存在著響應(yīng)的缺省值,要么其零值直接可以使用,且代表著特定的含義。
二、http.Client中的Transport字段
http.Client
類型中的Transport字段代表著:向網(wǎng)絡(luò)服務(wù)發(fā)送HTTP請(qǐng)求,并從網(wǎng)絡(luò)服務(wù)接收HTTP響應(yīng)的操作過(guò)程。
Transport字段的RoundTrip方法實(shí)現(xiàn)單次HTTP事務(wù)(或者說(shuō)基于HTTP協(xié)議的單詞交互)需要的所有步驟。
Transport 字段是http.RoundTrip
接口類型,它有一個(gè)缺省值,這個(gè)缺省值的變量名為DefaultTransport。DefaultTransport的實(shí)際類型為*http.Transport
,*http.Transport
可以被復(fù)用,并且是線程安全的。
如果沒(méi)有顯式的為
http.Client
中的Transport字段賦值,這個(gè)Client就會(huì)直接使DefaultTransport。
http.Client
中的Timeout字段,代表前面所說(shuō)的單詞HTTP事務(wù)的超時(shí)時(shí)間,它time.Duration
類型,它的零值是可用的,用于表示沒(méi)有設(shè)置超時(shí)時(shí)間。
(1)http.Transport
類型中的DialContext字段
http.Transport
類型,在內(nèi)部使用一個(gè)net.Dialer
類型的值,并且會(huì)把該值的Timeout字段的值,設(shè)定為30秒。
也就是說(shuō),這個(gè)Dialer值如果在30秒內(nèi)還沒(méi)有建立好網(wǎng)絡(luò)連接,那么就會(huì)被判定為操作超時(shí)。
在DefaultTransport的值被初始化的時(shí)候,這樣的Dialer值的DialContext方法會(huì)被賦給前者DialContext字段:
var DefaultTransport RoundTripper = &Transport{ Proxy: ProxyFromEnvironment, DialContext: defaultTransportDialContext(&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }), ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { return dialer.DialContext }
KeepAlive的背后是一種針對(duì)網(wǎng)絡(luò)連接(更確切地說(shuō),是TCP連接)的存活探測(cè)機(jī)制。它的值用于表示每隔多長(zhǎng)時(shí)間發(fā)送一次探測(cè)包。當(dāng)該值不大于0時(shí),則表示不開啟這種機(jī)制。
DefaultTransport會(huì)把這個(gè)字段的值設(shè)定為30秒。
(2)http.Transport
類型中的其它字段
一些是關(guān)于超時(shí)操作
IdleConnTimeout
:含義是空閑的連接在多久之后就應(yīng)該關(guān)閉。
DefaultTransport 會(huì)把該字段的值設(shè)定為90秒。
如果該值為0,那么就表示不關(guān)閉空閑連接。注意,這樣可能會(huì)造成資源的泄露。
ResponseHeaderTimeout
:含義是,從客戶端把請(qǐng)求完全遞交給操作系統(tǒng)到從操作系統(tǒng)那里接收到響應(yīng)報(bào)文頭到最長(zhǎng)時(shí)長(zhǎng)。
DefaultTransport并沒(méi)有設(shè)定該字段的值。
-
ExpectContinueTimeout
:含義是,在客戶端提交了請(qǐng)求報(bào)文頭之后,等待接收第一個(gè)響應(yīng)報(bào)文頭的最長(zhǎng)時(shí)間。
DefaultTransport 把該字段的值設(shè)定為1秒。
在客戶端想要使用HTTP的“POST”方法把一個(gè)很大的報(bào)文體發(fā)送給服務(wù)端的時(shí)候,它可以先通過(guò)發(fā)送一個(gè)包含了“Expect: 100-continue”的請(qǐng)求報(bào)文頭,來(lái)詢問(wèn)服務(wù)端是否愿意接受這個(gè)大報(bào)文體。這個(gè)字段就是用于設(shè)定在這種情況下的超時(shí)時(shí)間的。
注意,如果該字段的值不大于0,那么無(wú)論多大的請(qǐng)求報(bào)文體都將會(huì)被立即發(fā)送出去。
TLSHandshakeTimeout
:TLS是Transport Layer Security 的縮寫,可以被翻譯為傳輸層安全。這個(gè)字段代表了基于TLS協(xié)議的連接在被建立時(shí)的握手階段的超時(shí)時(shí)間。
DefaultTransport 把該字段的值設(shè)置為10秒。
若該值為0,則表示對(duì)這個(gè)值不設(shè)限。
一些與IdleConnTimeout
相關(guān)的字段值
MaxIdleConns
:用于控制訪問(wèn)所有主機(jī)的最大空閑連接。如果為0,不做限制。
DefaultTransport 把MaxIdleConns設(shè)定為100。
MaxIdleConns字段只會(huì)對(duì)空閑連接的總數(shù)做出限定。
MaxIdleConnsPerHost
: 控制Transport值訪問(wèn)每一個(gè)網(wǎng)絡(luò)服務(wù)的最大空閑連接數(shù)。如果為0,將使用缺省值2, 這個(gè)缺省值由DefaultMaxIdleConnsPerHost
所代表。
也就是說(shuō),默認(rèn)情況下,對(duì)于某一個(gè)Transport值訪問(wèn)的每一個(gè)網(wǎng)絡(luò)服務(wù),它的空閑連接數(shù)都最多只能由兩個(gè)。
MaxConnsPerHost
:針對(duì)某一個(gè)Transport值訪問(wèn)的每一個(gè)網(wǎng)絡(luò)服務(wù)的最大連接數(shù),不論這些連接是否是空閑的。
該字段沒(méi)有缺省值,零值表示不限定。
MaxIdleConns和MaxIdleConnsPerHost兩個(gè)與空閑連接數(shù)有關(guān)的字段的值應(yīng)該是聯(lián)動(dòng)的,所以,有時(shí)需要根據(jù)實(shí)際情況定制它們,可以參考DefaultTransport變量的聲明。
三、為什么會(huì)出現(xiàn)空閑的連接
3.1 空閑連接的產(chǎn)生
HTTP協(xié)議有一個(gè)請(qǐng)求報(bào)文頭,叫做“Connection”。在HTTP協(xié)議的1.1 版本中,這個(gè)報(bào)文頭的值默認(rèn)是“keep-alive”。
在這種情況下,網(wǎng)絡(luò)連接都是持久連接,它們會(huì)在當(dāng)前的HTTP事務(wù)完成后仍然保持著連通性,因此是可以被復(fù)用的。
連接的可復(fù)用,帶來(lái)兩種可能:
- 一種可能是,針對(duì)同一個(gè)網(wǎng)絡(luò)服務(wù),有新的HTTP請(qǐng)求被提交,該連接被再次使用。
- 另一種可能是,不再有對(duì)該網(wǎng)絡(luò)服務(wù)的HTTP請(qǐng)求,該連接被閑置。(產(chǎn)生空閑的連接)
后一種情況就產(chǎn)生了空閑連接。另外,如果分配給某一個(gè)網(wǎng)絡(luò)服務(wù)的連接過(guò)多的話,也可能會(huì)導(dǎo)致空閑連接的產(chǎn)生。因?yàn)槊恳粋€(gè)新遞交的HTTP請(qǐng)求,都只會(huì)征用一個(gè)空閑的連接。所以,為空閑連接設(shè)定限制,在大多數(shù)情況下都是很有必要的,也是需要斟酌的。
3.2 杜絕空閑連接的產(chǎn)生
如果想徹底杜絕空閑連接的產(chǎn)生,那么可以在初始化的時(shí)候,把它的DisableKeepAlives字段的值設(shè)定為true。這時(shí),HTTP請(qǐng)求的“Connection”報(bào)文頭的值就會(huì)被設(shè)置為“close”。這會(huì)告訴網(wǎng)絡(luò)服務(wù),這個(gè)網(wǎng)絡(luò)連接不必保持,當(dāng)前的HTTP事務(wù)完成后就可以斷開它。
如此一來(lái),每當(dāng)一個(gè)HTTP請(qǐng)求被遞交時(shí),就會(huì)產(chǎn)生一個(gè)新的網(wǎng)絡(luò)連接。這樣做會(huì)明顯地加重網(wǎng)絡(luò)服務(wù)以及客戶端的負(fù)載。所以,在一般情況下,我們都不要去設(shè)置這個(gè)DisableKeepAlive字段。
在net.Dialer類型中,也有一個(gè)看起來(lái)很相似的字段KeepAlive。不過(guò),它與前面所說(shuō)的HTTP 持久連接不是一個(gè)概念,KeepAlive是直接作用在底層的socket上的。
KeepAlive的背后是一種針對(duì)網(wǎng)絡(luò)連接(更確切地說(shuō),是TCP連接)的存活探測(cè)機(jī)制。它的值用于表示每隔多長(zhǎng)時(shí)間發(fā)送一次探測(cè)包。當(dāng)該值不大于0時(shí),則表示不開啟這種機(jī)制。DefaultTransport會(huì)把這個(gè)字段的值設(shè)定為30秒。
四、http.Server
http.Server類型與http.Client相對(duì)應(yīng)。http.Server代表的是基于HTTP協(xié)議的服務(wù)端,或者網(wǎng)絡(luò)服務(wù)。
4.1 http.Server類型的ListenAndServe方法
http.Server類型的ListenAndServe方法的功能是:監(jiān)聽一個(gè)基于TCP協(xié)議的網(wǎng)絡(luò)地址,并對(duì)接收到的HTTP請(qǐng)求進(jìn)行處理。
- 這個(gè)方法默認(rèn)會(huì)開啟針對(duì)網(wǎng)絡(luò)連接的存活探測(cè)機(jī)制,以保證連接是持久的。
- 同時(shí),該方法會(huì)一直執(zhí)行,直到有嚴(yán)重的錯(cuò)誤發(fā)生或被外界關(guān)掉。
當(dāng)被外界關(guān)掉時(shí),它會(huì)返回一個(gè)由http.ErrServerClosed
變量代表的錯(cuò)誤值。
4.2 ListenAndServe方法主要做的事情
func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(ln) }
ListenAndServe
方法主要會(huì)做下面的事情:
- 檢查當(dāng)前的
http.Server
類型的值的Addr字段。
該字段的值代表了當(dāng)前的網(wǎng)絡(luò)服務(wù)需要使用的網(wǎng)絡(luò)地址。即:IP地址和端口號(hào)。如果這個(gè)字段的值為空字符串,那么就用":http"代替。
也就是說(shuō),使用任何可以代表本機(jī)的域名和IP地址,并且端口號(hào)為80.
- 通過(guò)調(diào)用
net.Listen
函數(shù)在已確定的網(wǎng)絡(luò)地址上啟動(dòng)基于TCP協(xié)議的監(jiān)聽。 - 檢查
net.Listen
函數(shù)返回的錯(cuò)誤值。
如果該錯(cuò)誤值不為nil,那么就直接返回該值。否則,通過(guò)調(diào)用當(dāng)前值的Serve方法準(zhǔn)備接受和處理將要到來(lái)的HTTP請(qǐng)求。
4.3 (衍生問(wèn)題)net.Listen 函數(shù)都做了哪些事情
net.Listen
函數(shù)做的事情:
- 解析參數(shù)值中包含的網(wǎng)絡(luò)地址隱含的IP地址和端口號(hào);
- 根據(jù)給定的網(wǎng)絡(luò)協(xié)議,確定監(jiān)聽的方法,并開始進(jìn)行監(jiān)聽;
這里還可以延伸到net.socket函數(shù),以及socket相關(guān)的知識(shí)。
4.4 (衍生問(wèn)題)http.Server類型的Serve方法是怎么接受和處理HTTP請(qǐng)求的
在一個(gè)for循環(huán)中,網(wǎng)絡(luò)監(jiān)聽的Accept方法會(huì)被不斷的調(diào)用,
for { rw, err := l.Accept() }
該方法會(huì)返回兩個(gè)結(jié)果值:
- 第一個(gè)結(jié)果值是net.Conn 類型,代表包含了新到來(lái)的HTTP請(qǐng)求的網(wǎng)絡(luò)連接;
- 第二個(gè)結(jié)果值是error類型值,代表可能發(fā)生的錯(cuò)誤。
如果錯(cuò)誤不為nil,除非它代表了一個(gè)暫時(shí)性的錯(cuò)誤,否則循環(huán)都會(huì)被終止。如果是暫時(shí)性的錯(cuò)誤,那么循環(huán)的下一次迭代將會(huì)在一段時(shí)間之后開始執(zhí)行。
如果這里的Accept方法沒(méi)有返回非nil的錯(cuò)誤值,那么這里的程序?qū)?huì)把它的第一個(gè)結(jié)果值包裝成一個(gè)*http.conn類型的值,然后通過(guò)在新的goroutine中調(diào)用這個(gè)*http.conn 類型值的serve方法,來(lái)對(duì)當(dāng)前的HTTP請(qǐng)求進(jìn)行處理。
HTTP請(qǐng)求相關(guān)的,更多的衍生問(wèn)題:
- 這個(gè)*http.conn類型值的狀態(tài)有幾種,分別代表著處理的哪個(gè)階段?
- 處理的過(guò)程中會(huì)用到哪些讀取器和寫入器,它們的作用分別是什么?
- 這里的程序是怎么調(diào)用我們自定義的處理函數(shù)的?
五、思考:怎么優(yōu)雅地停止基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)程序?
srv.Shutdown(context.Background())
的方式停止服務(wù),通過(guò)RegisterOnShutdown可添加服務(wù)停止時(shí)的調(diào)用。
以上就是Golang中基于HTTP協(xié)議的網(wǎng)絡(luò)服務(wù)的詳細(xì)內(nèi)容,更多關(guān)于Golang HTTP協(xié)議的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
我為什么喜歡Go語(yǔ)言(簡(jiǎn)潔的Go語(yǔ)言)
從2000年至今,也寫了11年代碼了,期間用過(guò)VB、Delphi、C#、C++、Ruby、Python,一直在尋找一門符合自己心意和理念的語(yǔ)言。我很在意寫代碼時(shí)的手感和執(zhí)行的效率,所以在Go出現(xiàn)之前一直沒(méi)有找到2014-10-10GO使用阿里云,解決go get下載項(xiàng)目慢或無(wú)法下載的情況
這篇文章主要介紹了GO使用阿里云,解決go get下載項(xiàng)目慢或無(wú)法下載的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理
這篇文章主要介紹了一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理,gron是一個(gè)比較小巧、靈活的定時(shí)任務(wù)庫(kù),可以執(zhí)行定時(shí)的、周期性的任務(wù)。gron提供簡(jiǎn)潔的、并發(fā)安全的接口2022-08-08Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解
這篇文章主要為大家介紹了Go創(chuàng)建Grpc鏈接池實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03解決Golang中g(shù)oroutine執(zhí)行速度的問(wèn)題
這篇文章主要介紹了解決Golang中g(shù)oroutine執(zhí)行速度的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05