Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn)
Go微服務(wù)網(wǎng)關(guān)
從核心原理理解網(wǎng)關(guān)的本質(zhì)
網(wǎng)關(guān)具備的基本功能:
- 支持多種協(xié)議代理:tcp/http/ websocket/grpc
- 支持多種負(fù)載均衡策略:輪詢,權(quán)重輪詢,hash一致性輪詢
- 支持下游的服務(wù)發(fā)現(xiàn):主動(dòng)探測 / 自動(dòng)服務(wù)發(fā)現(xiàn)
- 支持橫向擴(kuò)展: 加機(jī)器就能解決高并發(fā)
借助網(wǎng)關(guān)處理高可用,高并發(fā)
- 限流:請求QPS限制
- 熔斷:錯(cuò)誤率達(dá)閾值則服務(wù)熔斷
- 降級:確保核心業(yè)務(wù)可用
- 權(quán)限認(rèn)證:請求攔截
網(wǎng)絡(luò)基礎(chǔ)大綱
OSI七層網(wǎng)絡(luò)協(xié)議
經(jīng)典協(xié)議與數(shù)據(jù)包
http 協(xié)議
GET/HTTP/1.1 Host:www.baidu.com User-Agent:curl/7.55.1 Accept:*/*
Websocket握手協(xié)議
三次握手 與 四次揮手
三次握手的最主要的目的是保證連接是全雙工的,可靠更多的是通過重傳機(jī)制來保證的
因?yàn)檫B接是全雙工的,雙方必須都收到對方的FIN包及確認(rèn)才可關(guān)閉
TCP報(bào)文格式:
其中比較重要的字段有:
(1)序號(hào)(sequence number):Seq序號(hào),占32位,用來標(biāo)識(shí)從TCP源端向目的端發(fā)送的字節(jié)流,發(fā)起方發(fā)送數(shù)據(jù)時(shí)對此進(jìn)行標(biāo)記。
(2)確認(rèn)號(hào)(acknowledgement number):Ack序號(hào),占32位,只有ACK標(biāo)志位為1時(shí),確認(rèn)序號(hào)字段才有效,Ack=Seq+1。
(3)標(biāo)志位(Flags):共6個(gè),即URG、ACK、PSH、RST、SYN、FIN等。具體含義如下:
URG:緊急指針(urgent pointer)有效。ACK:確認(rèn)序號(hào)有效。PSH:接收方應(yīng)該盡快將這個(gè)報(bào)文交給應(yīng)用層。RST:重置連接。SYN:發(fā)起一個(gè)新連接。FIN:釋放一個(gè)連接。
需要注意的是:
不要將確認(rèn)序號(hào)Ack與標(biāo)志位中的ACK搞混了。確認(rèn)方Ack=發(fā)起方Seq+1,兩端配對。
三次握手連接:
(1)首先客戶端向服務(wù)器端發(fā)送一段TCP報(bào)文,其中:
- 標(biāo)記位為`SYN,表示“請求建立新連接”;
- 序號(hào)為Seq=X(X一般為1);
- 隨后客戶端進(jìn)入SYN-SENT階段。
(2)服務(wù)器端接收到來自客戶端的TCP報(bào)文之后,結(jié)束LISTEN階段。并返回一段TCP報(bào)文,其中:
- 標(biāo)志位為SYN和ACK,表示“確認(rèn)客戶端的報(bào)文Seq序號(hào)有效,服務(wù)器能正常接收客戶端發(fā)送的數(shù)據(jù),并同意創(chuàng)建新連接”(即告訴客戶端,服務(wù)器收到了你的數(shù)據(jù));
- 序號(hào)為Seq=y;
- 確認(rèn)號(hào)為Ack=x+1,表示收到客戶端的序號(hào)Seq并將其值加1作為自己確認(rèn)號(hào)Ack的值;隨后服務(wù)器端進(jìn)入SYN-RCVD階段。
(3)客戶端接收到來自服務(wù)器端的確認(rèn)收到數(shù)據(jù)的TCP報(bào)文之后,明確了從客戶端到服務(wù)器的數(shù)據(jù)傳輸是正常的,結(jié)束SYN-SENT階段。并返回最后一段TCP報(bào)文。其中:
- 標(biāo)志位為ACK,表示“確認(rèn)收到服務(wù)器端同意連接的信號(hào)”(即告訴服務(wù)器,我知道你收到我發(fā)的數(shù)據(jù)了);
- 序號(hào)為Seq=x+1,表示收到服務(wù)器端的確認(rèn)號(hào)Ack,并將其值作為自己的序號(hào)值;
- 確認(rèn)號(hào)為Ack=y+1,表示收到服務(wù)器端序號(hào)Seq,并將其值加1作為自己的確認(rèn)號(hào)Ack的值;
- 隨后客戶端進(jìn)入ESTABLISHED階段。
服務(wù)器收到來自客戶端的“確認(rèn)收到服務(wù)器數(shù)據(jù)”的TCP報(bào)文之后,明確了從服務(wù)器到客戶端的數(shù)據(jù)傳輸是正常的。結(jié)束SYN-SENT階段,進(jìn)入ESTABLISHED階段。
在客戶端與服務(wù)器端傳輸?shù)腡CP報(bào)文中,雙方的確認(rèn)號(hào)Ack和序號(hào)Seq的值,都是在彼此Ack和Seq值的基礎(chǔ)上進(jìn)行計(jì)算的,這樣做保證了TCP報(bào)文傳輸?shù)倪B貫性。一旦出現(xiàn)某一方發(fā)出的TCP報(bào)文丟失,便無法繼續(xù)"握手",以此確保了"三次握手"的順利完成。
四次揮手:
(1)首先客戶端想要釋放連接,向服務(wù)器端發(fā)送一段TCP報(bào)文,其中:
- 標(biāo)記位為FIN,表示“請求釋放連接“;
- 序號(hào)為Seq=U;
- 隨后客戶端進(jìn)入FIN-WAIT-1階段,即半關(guān)閉階段。并且停止在客戶端到服務(wù)器端方向上發(fā)送數(shù)據(jù),但是客戶端仍然能接收從服務(wù)器端傳輸過來的數(shù)據(jù)。注意:這里不發(fā)送的是正常連接時(shí)傳輸?shù)臄?shù)據(jù)(非確認(rèn)報(bào)文),而不是一切數(shù)據(jù),所以客戶端仍然能發(fā)送ACK確認(rèn)報(bào)文。
(2)服務(wù)器端接收到從客戶端發(fā)出的TCP報(bào)文之后,確認(rèn)了客戶端想要釋放連接,隨后服務(wù)器端結(jié)束ESTABLISHED階段,進(jìn)入CLOSE-WAIT階段(半關(guān)閉狀態(tài))并返回一段TCP報(bào)文,其中:
- 標(biāo)記位為ACK,表示“接收到客戶端發(fā)送的釋放連接的請求”;
- 序號(hào)為Seq=V;
- 確認(rèn)號(hào)為Ack=U+1,表示是在收到客戶端報(bào)文的基礎(chǔ)上,將其序號(hào)Seq值加1作為本段報(bào)文確認(rèn)號(hào)Ack的值;
- 隨后服務(wù)器端開始準(zhǔn)備釋放服務(wù)器端到客戶端方向上的連接。客戶端收到從服務(wù)器端發(fā)出的TCP報(bào)文之后,確認(rèn)了服務(wù)器收到了客戶端發(fā)出的釋放連接請求,隨后客戶端結(jié)束FIN-WAIT-1階段,進(jìn)入FIN-WAIT-2階段
前"兩次揮手"既讓服務(wù)器端知道了客戶端想要釋放連接,也讓客戶端知道了服務(wù)器端了解了自己想要釋放連接的請求。于是,可以確認(rèn)關(guān)閉客戶端到服務(wù)器端方向上的連接了
(3)服務(wù)器端自從發(fā)出ACK確認(rèn)報(bào)文之后,經(jīng)過CLOSED-WAIT階段,做好了釋放服務(wù)器端到客戶端方向上的連接準(zhǔn)備,再次向客戶端發(fā)出一段TCP報(bào)文,其中:
- 標(biāo)記位為FIN,ACK,表示“已經(jīng)準(zhǔn)備好釋放連接了”。注意:這里的ACK并不是確認(rèn)收到服務(wù)器端報(bào)文的確認(rèn)報(bào)文。
- 序號(hào)為Seq=W;
- 確認(rèn)號(hào)為Ack=U+1;表示是在收到客戶端報(bào)文的基礎(chǔ)上,將其序號(hào)Seq值加1作為本段報(bào)文確認(rèn)號(hào)Ack的值。
隨后服務(wù)器端結(jié)束CLOSE-WAIT階段,進(jìn)入LAST-ACK階段。并且停止在服務(wù)器端到客戶端的方向上發(fā)送數(shù)據(jù),但是服務(wù)器端仍然能夠接收從客戶端傳輸過來的數(shù)據(jù)。
(4)客戶端收到從服務(wù)器端發(fā)出的TCP報(bào)文,確認(rèn)了服務(wù)器端已做好釋放連接的準(zhǔn)備,結(jié)束FIN-WAIT-2階段,進(jìn)入TIME-WAIT階段,并向服務(wù)器端發(fā)送一段報(bào)文,其中:
- 標(biāo)記位為ACK,表示“接收到服務(wù)器準(zhǔn)備好釋放連接的信號(hào)”。
- 序號(hào)為Seq=U+1;表示是在收到了服務(wù)器端報(bào)文的基礎(chǔ)上,將其確認(rèn)號(hào)Ack值作為本段報(bào)文序號(hào)的值。
- 確認(rèn)號(hào)為Ack=W+1;表示是在收到了服務(wù)器端報(bào)文的基礎(chǔ)上,將其序號(hào)Seq值作為本段報(bào)文確認(rèn)號(hào)的值。隨后客戶端開始在TIME-WAIT階段等待2MSL
為什么要客戶端要等待2MSL呢?見后文。
服務(wù)器端收到從客戶端發(fā)出的TCP報(bào)文之后結(jié)束LAST-ACK階段,進(jìn)入CLOSED階段。由此正式確認(rèn)關(guān)閉服務(wù)器端到客戶端方向上的連接。
客戶端等待完2MSL之后,結(jié)束TIME-WAIT階段,進(jìn)入CLOSED階段,由此完成“四次揮手”。
后“兩次揮手”既讓客戶端知道了服務(wù)器端準(zhǔn)備好釋放連接了,也讓服務(wù)器端知道了客戶端了解了自己準(zhǔn)備好釋放連接了。于是,可以確認(rèn)關(guān)閉服務(wù)器端到客戶端方向上的連接了,由此完成“四次揮手”。
與“三次揮手”一樣,在客戶端與服務(wù)器端傳輸?shù)腡CP報(bào)文中,雙方的確認(rèn)號(hào)Ack和序號(hào)Seq的值,都是在彼此Ack和Seq值的基礎(chǔ)上進(jìn)行計(jì)算的,這樣做保證了TCP報(bào)文傳輸?shù)倪B貫性,一旦出現(xiàn)某一方發(fā)出的TCP報(bào)文丟失,便無法繼續(xù)"揮手",以此確保了"四次揮手"的順利完成。
為什么客戶端在TIME-WAIT階段要等2MSL?
- 為的是確認(rèn)服務(wù)器端是否收到客戶端發(fā)出的ACK確認(rèn)報(bào)文
- 保證TCP協(xié)議的全雙共連接能夠可靠關(guān)閉
- 保證這次連接的重復(fù)數(shù)據(jù)段從網(wǎng)絡(luò)中消失
當(dāng)客戶端發(fā)出最后的ACK確認(rèn)報(bào)文時(shí),并不能確定服務(wù)器端能夠收到該段報(bào)文。所以客戶端在發(fā)送完ACK確認(rèn)報(bào)文之后,會(huì)設(shè)置一個(gè)時(shí)長為2MSL的計(jì)時(shí)器。MSL指的是(最大的生命周期)Maximum Segment Lifetime:(30秒–1分鐘)一段TCP報(bào)文在傳輸過程中的最大生命周期。2MSL即是服務(wù)器端發(fā)出為FIN報(bào)文和客戶端發(fā)出的ACK確認(rèn)報(bào)文所能保持有效的最大時(shí)長。
服務(wù)器端在1MSL內(nèi)沒有收到客戶端發(fā)出的ACK確認(rèn)報(bào)文,就會(huì)再次向客戶端發(fā)出FIN報(bào)文;
如果客戶端在2MSL內(nèi),再次收到了來自服務(wù)器端的FIN報(bào)文,說明服務(wù)器端由于各種原因沒有接收到客戶端發(fā)出的ACK確認(rèn)報(bào)文??蛻舳嗽俅蜗蚍?wù)器端發(fā)出ACK確認(rèn)報(bào)文,計(jì)時(shí)器重置,重新開始2MSL的計(jì)時(shí);否則客戶端在2MSL內(nèi)沒有再次收到來自服務(wù)器端的FIN報(bào)文,說明服務(wù)器端正常接收了ACK確認(rèn)報(bào)文,客戶端可以進(jìn)入CLOSED階段,完成“四次揮手”。
所以,客戶端要經(jīng)歷時(shí)長為2SML的TIME-WAIT階段;這也是為什么客戶端比服務(wù)器端晚進(jìn)入CLOSED階段的原因
為啥會(huì)出現(xiàn)大量的close_wait
- 首先close_wait一般出現(xiàn)在被動(dòng)關(guān)閉方
- 并發(fā)請求太多導(dǎo)致
- 被動(dòng)關(guān)閉方未及時(shí)釋放端口資源導(dǎo)致
func main() { //1、監(jiān)聽端口 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %v\n", err) return } //2.建立套接字連接 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %v\n", err) continue } //3. 創(chuàng)建處理協(xié)程 go func(conn net.Conn) { defer conn.Close() //思考題:這里不填寫會(huì)有啥問題? //服務(wù)端就有一個(gè)close,wait狀態(tài),客戶端就有一個(gè)finally 狀態(tài) for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(buf[:n]) fmt.Printf("receive from client, data: %v\n", str) } }(conn) } }
TCP為啥需要流量控制
- 由于通訊雙方,網(wǎng)速不同。通訊方任一方發(fā)送過快都會(huì)導(dǎo)致對方的消息處理不過來,所以就需要數(shù)據(jù)放到緩沖區(qū)中
- 如果緩沖區(qū)滿了,發(fā)送方還在瘋狂發(fā)送,那接收方只能把數(shù)據(jù)包丟棄,因此我們需要控制發(fā)送速率
- 我們緩沖區(qū)剩余大小稱之為接收窗口,用變量win表示,如果win=0,則發(fā)送方停止發(fā)送
TCP 為啥需要擁塞控制
- 流量控制與擁塞控制是兩個(gè)概念,擁塞控制是調(diào)節(jié)網(wǎng)絡(luò)的負(fù)載
- 接收方網(wǎng)絡(luò)資源繁忙,因未及時(shí)響應(yīng)ACK導(dǎo)致發(fā)送方重傳大量的數(shù)據(jù),這樣將會(huì)導(dǎo)致網(wǎng)絡(luò)更加的擁堵
- 擁塞控制是動(dòng)態(tài)調(diào)整win大小,不只是依賴緩沖區(qū)大小去確定窗口大小
TCP 擁塞控制
- 慢開始和擁塞避免
- 快速重傳和快速恢復(fù)
優(yōu)化步驟3到步驟4:因?yàn)榫W(wǎng)絡(luò)擁塞,有24直接降到1 ,會(huì)造成堵塞
為啥會(huì)出現(xiàn)粘包,拆包,如何處理
粘包、拆包表現(xiàn)形式
現(xiàn)在假設(shè)客戶端向服務(wù)端連續(xù)發(fā)送了兩個(gè)數(shù)據(jù)包,用packet1和packet2來表示,那么服務(wù)端收到的數(shù)據(jù)可以分為三種,現(xiàn)列舉如下:
第一種情況,接收端正常收到兩個(gè)數(shù)據(jù)包,即沒有發(fā)生拆包和粘包的現(xiàn)象,此種情況不在本文的討論范圍內(nèi)。
第二種情況,接收端只收到一個(gè)數(shù)據(jù)包,由于TCP是不會(huì)出現(xiàn)丟包的,所以這一個(gè)數(shù)據(jù)包中包含了發(fā)送端發(fā)送的兩個(gè)數(shù)據(jù)包的信息,這種現(xiàn)象即為粘包。這種情況由于接收端不知道這兩個(gè)數(shù)據(jù)包的界限,所以對于接收端來說很難處理。
第三種情況,這種情況有兩種表現(xiàn)形式,如下圖。接收端收到了兩個(gè)數(shù)據(jù)包,但是這兩個(gè)數(shù)據(jù)包要么是不完整的,要么就是多出來一塊,這種情況即發(fā)生了拆包和粘包。這兩種情況如果不加特殊處理,對于接收端同樣是不好處理的。
產(chǎn)生tcp粘包和拆包的原因
我們知道tcp是以流動(dòng)的方式傳輸數(shù)據(jù),傳輸?shù)淖钚挝粸橐粋€(gè)報(bào)文段(segment)。tcp Header中有個(gè)Options標(biāo)識(shí)位,常見的標(biāo)識(shí)為mss(Maximum Segment Size)指的是,連接層每次傳輸?shù)臄?shù)據(jù)有個(gè)最大限制MTU(Maximum Transmission Unit),一般是1500比特,超過這個(gè)量要分成多個(gè)報(bào)文段,mss則是這個(gè)最大限制減去TCP的header,光是要傳輸?shù)臄?shù)據(jù)的大小,一般為1460比特。換算成字節(jié),也就是180多字節(jié)。
tcp為提高性能,發(fā)送端會(huì)將需要發(fā)送的數(shù)據(jù)發(fā)送到緩沖區(qū),等待緩沖區(qū)滿了之后,再將緩沖中的數(shù)據(jù)發(fā)送到接收方。同理,接收方也有緩沖區(qū)這樣的機(jī)制,來接收數(shù)據(jù)。
發(fā)生TCP粘包、拆包主要是由于下面一些原因:
- 應(yīng)用程序?qū)懭氲臄?shù)據(jù)大于套接字緩沖區(qū)大小,這將會(huì)發(fā)生拆包。
- 應(yīng)用程序?qū)懭霐?shù)據(jù)小于套接字緩沖區(qū)大小,網(wǎng)卡將應(yīng)用多次寫入的數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上,這將會(huì)發(fā)生粘包。
- 進(jìn)行mss(最大報(bào)文長度)大小的TCP分段,當(dāng)TCP報(bào)文長度-TCP頭部長度>mss的時(shí)候?qū)l(fā)生拆包。
- 接收方法不及時(shí)讀取套接字(socket)緩沖區(qū)數(shù)據(jù),這將發(fā)生粘包。
如何解決拆包粘包
既然知道了tcp是無界的數(shù)據(jù)流,且協(xié)議本身無法避免粘包,拆包的發(fā)生,那我們只能在應(yīng)用層數(shù)據(jù)協(xié)議上,加以控制。通常在制定傳輸數(shù)據(jù)時(shí),可以使用如下方法:
- 使用帶消息頭的協(xié)議、消息頭存儲(chǔ)消息開始標(biāo)識(shí)及消息長度信息,服務(wù)端獲取消息頭的時(shí)候解析出消息長度,然后向后讀取該長度的內(nèi)容。
- 設(shè)置定長消息,服務(wù)端每次讀取既定長度的內(nèi)容作為一條完整消息。
- 設(shè)置消息邊界,服務(wù)端從網(wǎng)絡(luò)流中按消息編輯分離出消息內(nèi)容。
如何獲取完整應(yīng)用數(shù)據(jù)報(bào)文
- 使用帶消息頭的協(xié)議,頭部寫入包長度,然后在讀取包內(nèi)容
- 設(shè)置定長消息,每次讀取定長內(nèi)容,長度不夠時(shí)空位補(bǔ)固定字符
- 設(shè)置消息邊界,服務(wù)端從網(wǎng)絡(luò)流中按消息邊界分離出消息內(nèi)容,一般使用 ‘\n’
- 更為復(fù)雜的協(xié)議:json,protobuf
如何獲取完整的數(shù)據(jù)報(bào)文
func main() { //類比接收緩沖區(qū) bytesBuffer := bytes.NewBuffer([]byte{}) // 發(fā)送 if err := Encode(bytesBuffer, "hello world 0 !!"); err != nil { panic(err) } if err := Encode(bytesBuffer, "hello world 1 !!"); err != nil { panic(err) } //讀取 for { if bt, err := Decode(bytesBuffer); err == nil { fmt.Println(string(bt)) continue } break } }
如何獲取完整的數(shù)據(jù)報(bào)文
tcp_server
func main() { //simple tcp server //1.監(jiān)聽端口 listener, err := net.Listen("tcp", "127.0.0.1:9090") if err != nil { fmt.Printf("tcp Listen fail,err: %v\n", err) return } //2.接受請求 for { conn, err := listener.Accept() if err != nil { fmt.Printf("tcp Accept fail,err: %v\n", err) continue } //3.創(chuàng)建協(xié)程 go process(conn) } } //4.創(chuàng)建的協(xié)程里面實(shí)現(xiàn)解碼的功能 func process(conn net.Conn) { defer conn.Close() for { bt, err := unpack.Decode(conn) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(bt) fmt.Printf("receive from client, data: %v\n", str) } }
tcp_client
func main() { //1.連接tcp服務(wù)器 conn, err := net.Dial("tcp", "localhost:9090") defer conn.Close() if err != nil { fmt.Printf("connect failed, err : %v\n", err.Error()) return } //2.實(shí)現(xiàn)編碼 unpack.Encode(conn, "hello world 0!!!") }
**unpack ** : 實(shí)現(xiàn)編碼(encode)和解碼(docode)功能
const Msg_Header = "12345678" // 編碼 func Encode(bytesBuffer io.Writer, content string) error { //msg_header+content_len+content //8+4+content_len if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(Msg_Header)); err != nil { return err } clen := int32(len([]byte(content))) // binary.BigEndian 大端字節(jié)實(shí)現(xiàn)的加密 , if err := binary.Write(bytesBuffer, binary.BigEndian, clen); err != nil { return err } if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(content)); err != nil { return err } return nil } // 解碼 func Decode(bytesBuffer io.Reader) (bodyBuf []byte, err error) { MagicBuf := make([]byte, len(Msg_Header)) //先讀取header的大小 if _, err = io.ReadFull(bytesBuffer, MagicBuf); err != nil { return nil, err } //比較得到的header和實(shí)際的Msg_Header 是否相同 if string(MagicBuf) != Msg_Header { return nil, errors.New("msg_header error") } lengthBuf := make([]byte, 4) if _, err = io.ReadFull(bytesBuffer, lengthBuf); err != nil { return nil, err } // binary.BigEndian 大端字節(jié)實(shí)現(xiàn)的解密 ,得到實(shí)際數(shù)據(jù)的長度 length := binary.BigEndian.Uint32(lengthBuf) bodyBuf = make([]byte, length) if _, err = io.ReadFull(bytesBuffer, bodyBuf); err != nil { return nil, err } return bodyBuf, err }
基于golang 實(shí)現(xiàn)TCP,UDP,Http服務(wù)端與客戶端
golang 實(shí)現(xiàn)UDP 服務(wù)端與客戶端
UDP服務(wù)端:
func main() { //1.監(jiān)聽端口 listen, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 9090, }) if err != nil { fmt.Printf("listen udp failed ,err:%v\n", err) return } //2.循環(huán)讀取消息內(nèi)容 for { var data [1024]byte n, addr, err := listen.ReadFromUDP(data[:]) if err != nil { fmt.Printf("read failed from addr :%v,err%v\n", addr, err) break } go func() { //3.回復(fù)數(shù)據(jù) fmt.Printf("addr:%v data:%v count:%v\n", addr, string(data[:n]), n) _, err = listen.WriteToUDP([]byte("received success!"), addr) if err != nil { fmt.Printf("write failed,err :%v\n", err) return } }() } }
udp客戶端
func main() { //1. 連接udp服務(wù)器 conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 9090, }) if err != nil { fmt.Printf("connect failed ,err %v\n", err) return } for i := 0; i < 100; i++ { // 2.發(fā)送數(shù)據(jù) _, err := conn.Write([]byte("hello " + "server")) if err != nil { fmt.Printf("send data failed,err: %v\n", err) return } // 3. 接收數(shù)據(jù) result := make([]byte, 1024) n, remoteAddr, err := conn.ReadFromUDP(result) if err != nil { fmt.Printf("read data failed,err:%v\n", err) return } fmt.Printf("receive from addr:%v data:%v\n", remoteAddr, string(result[:n])) } }
golang實(shí)現(xiàn)tcp的服務(wù)端和客戶端
tcp 服務(wù)端
func main() { //1、監(jiān)聽端口 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %v\n", err) return } //2.建立套接字連接 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %v\n", err) continue } //3. 創(chuàng)建處理協(xié)程 go process(conn) } } func process(conn net.Conn) { defer conn.Close() //思考題:這里不填寫會(huì)有啥問題? for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(buf[:n]) fmt.Printf(" from client, data: %v\n", str) } }
tcp客戶端
golang實(shí)現(xiàn)Http的服務(wù)端和客戶端
http服務(wù)端
var ( Addr = ":8000" ) // http的服務(wù)器 func main() { //1.創(chuàng)建路由器 mux := http.NewServeMux() // 2. 設(shè)置路由規(guī)則 mux.HandleFunc("/bye", sayBye) // 3.創(chuàng)建服務(wù)器 server := &http.Server{ Addr: Addr, WriteTimeout: time.Second * 3, Handler: mux, } // 4. 監(jiān)聽端口并提供服務(wù) log.Println("starting httpServer at" + Addr) log.Fatal(server.ListenAndServe()) } func sayBye(w http.ResponseWriter, r *http.Request) { time.Sleep(1 * time.Second) w.Write([]byte("bye bye,this is httpserver")) }
http客戶端
func main() { //1. 創(chuàng)建連接池 transport := &http.Transport{ DialContext: (&net.Dialer{ Timeout: 30 * time.Second, // 超時(shí)時(shí)間 KeepAlive: 30 * time.Second, //長連接時(shí)間 }).DialContext, MaxIdleConns: 100, //最大空閑連接數(shù) IdleConnTimeout: 90 * time.Second, // 空閑超時(shí)時(shí)間 TLSHandshakeTimeout: 10 * time.Second, // tls握手超時(shí)時(shí)間 ExpectContinueTimeout: 1 * time.Second, // 100-continue 狀態(tài)碼超時(shí)時(shí)間 } //2. 創(chuàng)建客戶端 client := &http.Client{ Timeout: 30 * time.Second, Transport: transport, } //3.請求數(shù)據(jù) resp, err := client.Get("http://127.0.0.1:8000/bye") if err != nil { fmt.Println("client get url failed ", err) return } defer resp.Body.Close() //4.讀取內(nèi)容 b, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Read body failed ", err) return } fmt.Println(string(b)) }
Http 服務(wù)器源碼解讀
- 閱讀源代碼的原則:先整體在局部,先看腦圖在逐一分析
- 注冊路由:理解函數(shù)是一等公民以及注冊原理
- 開啟服務(wù)
- 處理連接
函數(shù)是一等公民
type HandleFunc func(http.ResponseWriter, *http.Request) func (f HandleFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) } //函數(shù)是一等公民 func main() { hf := HandleFunc(HelloHandler) resp := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", bytes.NewBuffer([]byte("test"))) hf.ServerHTTP(resp, req) b, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(b)) } func HelloHandler(res http.ResponseWriter, req *http.Request) { res.Write([]byte("hello youMe ")) }
到此這篇關(guān)于Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go微服務(wù)網(wǎng)關(guān)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go?doudou開發(fā)gRPC服務(wù)快速上手實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了go?doudou開發(fā)gRPC服務(wù)快速上手實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Go習(xí)慣用法(多值賦值短變量聲明賦值簡寫模式)基礎(chǔ)實(shí)例
本文為大家介紹了Go習(xí)慣用法(多值賦值,短變量聲明和賦值,簡寫模式、多值返回函數(shù)、comma,ok 表達(dá)式、傳值規(guī)則)的基礎(chǔ)實(shí)例,幫大家鞏固扎實(shí)Go語言基礎(chǔ)2024-01-01Go 使用Unmarshal將json賦給struct出錯(cuò)的原因及解決
這篇文章主要介紹了Go 使用Unmarshal將json賦給struct出錯(cuò)的原因及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03淺析Go設(shè)計(jì)模式之Facade(外觀)模式
本文將介紹外觀模式的概念、結(jié)構(gòu)和工作原理,并提供一些在Go中實(shí)現(xiàn)外觀模式的示例代碼,通過使用外觀模式,可以降低代碼的耦合度,提高代碼的可維護(hù)性和可讀性,需要的朋友可以參考下2023-05-05Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法
這篇文章主要介紹了Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法,實(shí)例分析了Go語言進(jìn)行ip驗(yàn)證的技巧,需要的朋友可以參考下2015-03-03golang協(xié)程關(guān)閉踩坑實(shí)戰(zhàn)記錄
協(xié)程(coroutine)是Go語言中的輕量級線程實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03