" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn)

 更新時(shí)間:2022年07月11日 10:01:42   作者:小馨whisper  
本文主要介紹了Go微服務(wù)網(wǎng)關(guān)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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ù)包

?    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-geNZSVqb-1648194571597)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647745113748.png)]

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ā)送

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-VPPRtwrp-1648194571601)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647790417307.png)]

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ì)造成堵塞

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-g8ZHBvyy-1648194571602)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647791444149.png)]

為啥會(huì)出現(xiàn)粘包,拆包,如何處理

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-5vWmWpIu-1648194571603)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647791724023.png)]

粘包、拆包表現(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)。

img

第二種情況,接收端只收到一個(gè)數(shù)據(jù)包,由于TCP是不會(huì)出現(xiàn)丟包的,所以這一個(gè)數(shù)據(jù)包中包含了發(fā)送端發(fā)送的兩個(gè)數(shù)據(jù)包的信息,這種現(xiàn)象即為粘包。這種情況由于接收端不知道這兩個(gè)數(shù)據(jù)包的界限,所以對于接收端來說很難處理。

img

第三種情況,這種情況有兩種表現(xiàn)形式,如下圖。接收端收到了兩個(gè)數(shù)據(jù)包,但是這兩個(gè)數(shù)據(jù)包要么是不完整的,要么就是多出來一塊,這種情況即發(fā)生了拆包和粘包。這兩種情況如果不加特殊處理,對于接收端同樣是不好處理的。

img

產(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

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-FNUamEZv-1648194571603)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647843018454.png)]

如何獲取完整的數(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ù)端與客戶端

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-LR962weH-1648194571604)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647852602546.png)]

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ù)端和客戶端

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-vIA4cqIv-1648194571605)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647858500637.png)]

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ù)端和客戶端

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-QUVM7LpJ-1648194571605)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647960142348.png)]

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 "))
}

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-MjVQl5gV-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017007628.png)]

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-7JakmfrM-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017357816.png)]

到此這篇關(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)詳解

    這篇文章主要為大家介紹了go?doudou開發(fā)gRPC服務(wù)快速上手實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Go習(xí)慣用法(多值賦值短變量聲明賦值簡寫模式)基礎(chǔ)實(shí)例

    Go習(xí)慣用法(多值賦值短變量聲明賦值簡寫模式)基礎(chǔ)實(shí)例

    本文為大家介紹了Go習(xí)慣用法(多值賦值,短變量聲明和賦值,簡寫模式、多值返回函數(shù)、comma,ok 表達(dá)式、傳值規(guī)則)的基礎(chǔ)實(shí)例,幫大家鞏固扎實(shí)Go語言基礎(chǔ)
    2024-01-01
  • Go1.21新增slices包中函數(shù)的用法詳解

    Go1.21新增slices包中函數(shù)的用法詳解

    Go?1.21新增的?slices?包提供了很多和切片相關(guān)的函數(shù),可以用于任何類型的切片,本文為大家整理了部分函數(shù)的具體用法,感興趣的小伙伴可以了解一下
    2023-08-08
  • Go 使用Unmarshal將json賦給struct出錯(cuò)的原因及解決

    Go 使用Unmarshal將json賦給struct出錯(cuò)的原因及解決

    這篇文章主要介紹了Go 使用Unmarshal將json賦給struct出錯(cuò)的原因及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • go?字符串修改的操作代碼

    go?字符串修改的操作代碼

    這篇文章主要介紹了go?字符串修改,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 淺析Go設(shè)計(jì)模式之Facade(外觀)模式

    淺析Go設(shè)計(jì)模式之Facade(外觀)模式

    本文將介紹外觀模式的概念、結(jié)構(gòu)和工作原理,并提供一些在Go中實(shí)現(xiàn)外觀模式的示例代碼,通過使用外觀模式,可以降低代碼的耦合度,提高代碼的可維護(hù)性和可讀性,需要的朋友可以參考下
    2023-05-05
  • 解析Go語言編程中的struct結(jié)構(gòu)

    解析Go語言編程中的struct結(jié)構(gòu)

    這篇文章主要介紹了Go語言編程中的struct結(jié)構(gòu),是Go語言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法

    Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法

    這篇文章主要介紹了Go語言RPC Authorization進(jìn)行簡單ip安全驗(yàn)證的方法,實(shí)例分析了Go語言進(jìn)行ip驗(yàn)證的技巧,需要的朋友可以參考下
    2015-03-03
  • 使用Go語言編寫HTTP中間件的示例詳解

    使用Go語言編寫HTTP中間件的示例詳解

    在Go語言中,HTTP中間件是一種處理HTTP請求和響應(yīng)的函數(shù),它可以攔截到請求并對其進(jìn)行處理,然后再將請求傳遞給下一個(gè)中間件或目標(biāo)處理程序,本文給大家介紹了使用Go語言編寫HTTP中間件的示例,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-01-01
  • golang協(xié)程關(guān)閉踩坑實(shí)戰(zhàn)記錄

    golang協(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

最新評論