" />

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

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

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

Go微服務網(wǎng)關(guān)

從核心原理理解網(wǎng)關(guān)的本質(zhì)

網(wǎng)關(guān)具備的基本功能:

  • 支持多種協(xié)議代理:tcp/http/ websocket/grpc
  • 支持多種負載均衡策略:輪詢,權(quán)重輪詢,hash一致性輪詢
  • 支持下游的服務發(fā)現(xiàn):主動探測 / 自動服務發(fā)現(xiàn)
  • 支持橫向擴展: 加機器就能解決高并發(fā)

借助網(wǎng)關(guān)處理高可用,高并發(fā)

  • 限流:請求QPS限制
  • 熔斷:錯誤率達閾值則服務熔斷
  • 降級:確保核心業(yè)務可用
  • 權(quán)限認證:請求攔截

網(wǎng)絡基礎(chǔ)大綱

OSI七層網(wǎng)絡協(xié)議

在這里插入圖片描述

經(jīng)典協(xié)議與數(shù)據(jù)包

?    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(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é)議

在這里插入圖片描述

在這里插入圖片描述

三次握手 與 四次揮手

三次握手的最主要的目的是保證連接是全雙工的,可靠更多的是通過重傳機制來保證的

因為連接是全雙工的,雙方必須都收到對方的FIN包及確認才可關(guān)閉

TCP報文格式:

在這里插入圖片描述

其中比較重要的字段有:

(1)序號(sequence number):Seq序號,占32位,用來標識從TCP源端向目的端發(fā)送的字節(jié)流,發(fā)起方發(fā)送數(shù)據(jù)時對此進行標記。

(2)確認號(acknowledgement number):Ack序號,占32位,只有ACK標志位為1時,確認序號字段才有效,Ack=Seq+1。

(3)標志位(Flags):共6個,即URG、ACK、PSH、RST、SYN、FIN等。具體含義如下:

URG:緊急指針(urgent pointer)有效。ACK:確認序號有效。PSH:接收方應該盡快將這個報文交給應用層。RST:重置連接。SYN:發(fā)起一個新連接。FIN:釋放一個連接。

需要注意的是:

不要將確認序號Ack與標志位中的ACK搞混了。確認方Ack=發(fā)起方Seq+1,兩端配對。

三次握手連接:

在這里插入圖片描述

(1)首先客戶端向服務器端發(fā)送一段TCP報文,其中:

  • 標記位為`SYN,表示“請求建立新連接”;
  • 序號為Seq=X(X一般為1);
  • 隨后客戶端進入SYN-SENT階段。

(2)服務器端接收到來自客戶端的TCP報文之后,結(jié)束LISTEN階段。并返回一段TCP報文,其中:

  • 標志位為SYN和ACK,表示“確認客戶端的報文Seq序號有效,服務器能正常接收客戶端發(fā)送的數(shù)據(jù),并同意創(chuàng)建新連接”(即告訴客戶端,服務器收到了你的數(shù)據(jù));
  • 序號為Seq=y;
  • 確認號為Ack=x+1,表示收到客戶端的序號Seq并將其值加1作為自己確認號Ack的值;隨后服務器端進入SYN-RCVD階段。

(3)客戶端接收到來自服務器端的確認收到數(shù)據(jù)的TCP報文之后,明確了從客戶端到服務器的數(shù)據(jù)傳輸是正常的,結(jié)束SYN-SENT階段。并返回最后一段TCP報文。其中:

  • 標志位為ACK,表示“確認收到服務器端同意連接的信號”(即告訴服務器,我知道你收到我發(fā)的數(shù)據(jù)了);
  • 序號為Seq=x+1,表示收到服務器端的確認號Ack,并將其值作為自己的序號值;
  • 確認號為Ack=y+1,表示收到服務器端序號Seq,并將其值加1作為自己的確認號Ack的值;
  • 隨后客戶端進入ESTABLISHED階段。

服務器收到來自客戶端的“確認收到服務器數(shù)據(jù)”的TCP報文之后,明確了從服務器到客戶端的數(shù)據(jù)傳輸是正常的。結(jié)束SYN-SENT階段,進入ESTABLISHED階段。

在客戶端與服務器端傳輸?shù)腡CP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎(chǔ)上進行計算的,這樣做保證了TCP報文傳輸?shù)倪B貫性。一旦出現(xiàn)某一方發(fā)出的TCP報文丟失,便無法繼續(xù)"握手",以此確保了"三次握手"的順利完成。

四次揮手:

在這里插入圖片描述

(1)首先客戶端想要釋放連接,向服務器端發(fā)送一段TCP報文,其中:

  • 標記位為FIN,表示“請求釋放連接“;
  • 序號為Seq=U;
  • 隨后客戶端進入FIN-WAIT-1階段,即半關(guān)閉階段。并且停止在客戶端到服務器端方向上發(fā)送數(shù)據(jù),但是客戶端仍然能接收從服務器端傳輸過來的數(shù)據(jù)。注意:這里不發(fā)送的是正常連接時傳輸?shù)臄?shù)據(jù)(非確認報文),而不是一切數(shù)據(jù),所以客戶端仍然能發(fā)送ACK確認報文。

(2)服務器端接收到從客戶端發(fā)出的TCP報文之后,確認了客戶端想要釋放連接,隨后服務器端結(jié)束ESTABLISHED階段,進入CLOSE-WAIT階段(半關(guān)閉狀態(tài))并返回一段TCP報文,其中:

  • 標記位為ACK,表示“接收到客戶端發(fā)送的釋放連接的請求”;
  • 序號為Seq=V;
  • 確認號為Ack=U+1,表示是在收到客戶端報文的基礎(chǔ)上,將其序號Seq值加1作為本段報文確認號Ack的值;
  • 隨后服務器端開始準備釋放服務器端到客戶端方向上的連接??蛻舳耸盏綇姆掌鞫税l(fā)出的TCP報文之后,確認了服務器收到了客戶端發(fā)出的釋放連接請求,隨后客戶端結(jié)束FIN-WAIT-1階段,進入FIN-WAIT-2階段

前"兩次揮手"既讓服務器端知道了客戶端想要釋放連接,也讓客戶端知道了服務器端了解了自己想要釋放連接的請求。于是,可以確認關(guān)閉客戶端到服務器端方向上的連接了

(3)服務器端自從發(fā)出ACK確認報文之后,經(jīng)過CLOSED-WAIT階段,做好了釋放服務器端到客戶端方向上的連接準備,再次向客戶端發(fā)出一段TCP報文,其中:

  • 標記位為FIN,ACK,表示“已經(jīng)準備好釋放連接了”。注意:這里的ACK并不是確認收到服務器端報文的確認報文。
  • 序號為Seq=W;
  • 確認號為Ack=U+1;表示是在收到客戶端報文的基礎(chǔ)上,將其序號Seq值加1作為本段報文確認號Ack的值。

隨后服務器端結(jié)束CLOSE-WAIT階段,進入LAST-ACK階段。并且停止在服務器端到客戶端的方向上發(fā)送數(shù)據(jù),但是服務器端仍然能夠接收從客戶端傳輸過來的數(shù)據(jù)。

(4)客戶端收到從服務器端發(fā)出的TCP報文,確認了服務器端已做好釋放連接的準備,結(jié)束FIN-WAIT-2階段,進入TIME-WAIT階段,并向服務器端發(fā)送一段報文,其中:

  • 標記位為ACK,表示“接收到服務器準備好釋放連接的信號”。
  • 序號為Seq=U+1;表示是在收到了服務器端報文的基礎(chǔ)上,將其確認號Ack值作為本段報文序號的值。
  • 確認號為Ack=W+1;表示是在收到了服務器端報文的基礎(chǔ)上,將其序號Seq值作為本段報文確認號的值。隨后客戶端開始在TIME-WAIT階段等待2MSL

為什么要客戶端要等待2MSL呢?見后文。

服務器端收到從客戶端發(fā)出的TCP報文之后結(jié)束LAST-ACK階段,進入CLOSED階段。由此正式確認關(guān)閉服務器端到客戶端方向上的連接。

客戶端等待完2MSL之后,結(jié)束TIME-WAIT階段,進入CLOSED階段,由此完成“四次揮手”。

后“兩次揮手”既讓客戶端知道了服務器端準備好釋放連接了,也讓服務器端知道了客戶端了解了自己準備好釋放連接了。于是,可以確認關(guān)閉服務器端到客戶端方向上的連接了,由此完成“四次揮手”。

與“三次揮手”一樣,在客戶端與服務器端傳輸?shù)腡CP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎(chǔ)上進行計算的,這樣做保證了TCP報文傳輸?shù)倪B貫性,一旦出現(xiàn)某一方發(fā)出的TCP報文丟失,便無法繼續(xù)"揮手",以此確保了"四次揮手"的順利完成。

為什么客戶端在TIME-WAIT階段要等2MSL?

  • 為的是確認服務器端是否收到客戶端發(fā)出的ACK確認報文
  • 保證TCP協(xié)議的全雙共連接能夠可靠關(guān)閉
  • 保證這次連接的重復數(shù)據(jù)段從網(wǎng)絡中消失

當客戶端發(fā)出最后的ACK確認報文時,并不能確定服務器端能夠收到該段報文。所以客戶端在發(fā)送完ACK確認報文之后,會設(shè)置一個時長為2MSL的計時器。MSL指的是(最大的生命周期)Maximum Segment Lifetime:(30秒–1分鐘)一段TCP報文在傳輸過程中的最大生命周期。2MSL即是服務器端發(fā)出為FIN報文和客戶端發(fā)出的ACK確認報文所能保持有效的最大時長。

服務器端在1MSL內(nèi)沒有收到客戶端發(fā)出的ACK確認報文,就會再次向客戶端發(fā)出FIN報文;

如果客戶端在2MSL內(nèi),再次收到了來自服務器端的FIN報文,說明服務器端由于各種原因沒有接收到客戶端發(fā)出的ACK確認報文。客戶端再次向服務器端發(fā)出ACK確認報文,計時器重置,重新開始2MSL的計時;否則客戶端在2MSL內(nèi)沒有再次收到來自服務器端的FIN報文,說明服務器端正常接收了ACK確認報文,客戶端可以進入CLOSED階段,完成“四次揮手”。

所以,客戶端要經(jīng)歷時長為2SML的TIME-WAIT階段;這也是為什么客戶端比服務器端晚進入CLOSED階段的原因

為啥會出現(xiàn)大量的close_wait

  • 首先close_wait一般出現(xiàn)在被動關(guān)閉方
  • 并發(fā)請求太多導致
  • 被動關(guān)閉方未及時釋放端口資源導致
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() //思考題:這里不填寫會有啥問題?
            //服務端就有一個close,wait狀態(tài),客戶端就有一個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ā)送過快都會導致對方的消息處理不過來,所以就需要數(shù)據(jù)放到緩沖區(qū)中
  • 如果緩沖區(qū)滿了,發(fā)送方還在瘋狂發(fā)送,那接收方只能把數(shù)據(jù)包丟棄,因此我們需要控制發(fā)送速率
  • 我們緩沖區(qū)剩余大小稱之為接收窗口,用變量win表示,如果win=0,則發(fā)送方停止發(fā)送

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

TCP 為啥需要擁塞控制

  • 流量控制與擁塞控制是兩個概念,擁塞控制是調(diào)節(jié)網(wǎng)絡的負載
  • 接收方網(wǎng)絡資源繁忙,因未及時響應ACK導致發(fā)送方重傳大量的數(shù)據(jù),這樣將會導致網(wǎng)絡更加的擁堵
  • 擁塞控制是動態(tài)調(diào)整win大小,不只是依賴緩沖區(qū)大小去確定窗口大小

TCP 擁塞控制

  • 慢開始和擁塞避免
  • 快速重傳和快速恢復

在這里插入圖片描述

在這里插入圖片描述

優(yōu)化步驟3到步驟4:因為網(wǎng)絡擁塞,有24直接降到1 ,會造成堵塞

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

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

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

粘包、拆包表現(xiàn)形式

現(xiàn)在假設(shè)客戶端向服務端連續(xù)發(fā)送了兩個數(shù)據(jù)包,用packet1和packet2來表示,那么服務端收到的數(shù)據(jù)可以分為三種,現(xiàn)列舉如下:

第一種情況,接收端正常收到兩個數(shù)據(jù)包,即沒有發(fā)生拆包和粘包的現(xiàn)象,此種情況不在本文的討論范圍內(nèi)。

img

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

img

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

img

產(chǎn)生tcp粘包和拆包的原因

我們知道tcp是以流動的方式傳輸數(shù)據(jù),傳輸?shù)淖钚挝粸橐粋€報文段(segment)。tcp Header中有個Options標識位,常見的標識為mss(Maximum Segment Size)指的是,連接層每次傳輸?shù)臄?shù)據(jù)有個最大限制MTU(Maximum Transmission Unit),一般是1500比特,超過這個量要分成多個報文段,mss則是這個最大限制減去TCP的header,光是要傳輸?shù)臄?shù)據(jù)的大小,一般為1460比特。換算成字節(jié),也就是180多字節(jié)。

tcp為提高性能,發(fā)送端會將需要發(fā)送的數(shù)據(jù)發(fā)送到緩沖區(qū),等待緩沖區(qū)滿了之后,再將緩沖中的數(shù)據(jù)發(fā)送到接收方。同理,接收方也有緩沖區(qū)這樣的機制,來接收數(shù)據(jù)。

發(fā)生TCP粘包、拆包主要是由于下面一些原因:

  • 應用程序?qū)懭氲臄?shù)據(jù)大于套接字緩沖區(qū)大小,這將會發(fā)生拆包。
  • 應用程序?qū)懭霐?shù)據(jù)小于套接字緩沖區(qū)大小,網(wǎng)卡將應用多次寫入的數(shù)據(jù)發(fā)送到網(wǎng)絡上,這將會發(fā)生粘包。
  • 進行mss(最大報文長度)大小的TCP分段,當TCP報文長度-TCP頭部長度>mss的時候?qū)l(fā)生拆包。
  • 接收方法不及時讀取套接字(socket)緩沖區(qū)數(shù)據(jù),這將發(fā)生粘包。

如何解決拆包粘包

既然知道了tcp是無界的數(shù)據(jù)流,且協(xié)議本身無法避免粘包,拆包的發(fā)生,那我們只能在應用層數(shù)據(jù)協(xié)議上,加以控制。通常在制定傳輸數(shù)據(jù)時,可以使用如下方法:

  • 使用帶消息頭的協(xié)議、消息頭存儲消息開始標識及消息長度信息,服務端獲取消息頭的時候解析出消息長度,然后向后讀取該長度的內(nèi)容。
  • 設(shè)置定長消息,服務端每次讀取既定長度的內(nèi)容作為一條完整消息。
  • 設(shè)置消息邊界,服務端從網(wǎng)絡流中按消息編輯分離出消息內(nèi)容。

如何獲取完整應用數(shù)據(jù)報文

  • 使用帶消息頭的協(xié)議,頭部寫入包長度,然后在讀取包內(nèi)容
  • 設(shè)置定長消息,每次讀取定長內(nèi)容,長度不夠時空位補固定字符
  • 設(shè)置消息邊界,服務端從網(wǎng)絡流中按消息邊界分離出消息內(nèi)容,一般使用 ‘\n’
  • 更為復雜的協(xié)議:json,protobuf

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

如何獲取完整的數(shù)據(jù)報文

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ù)報文

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é)程里面實現(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服務器
	conn, err := net.Dial("tcp", "localhost:9090")
	defer conn.Close()
	if err != nil {
		fmt.Printf("connect failed, err : %v\n", err.Error())
		return
	}
	//2.實現(xiàn)編碼
	unpack.Encode(conn, "hello world 0!!!")
}

**unpack ** : 實現(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é)實現(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和實際的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é)實現(xiàn)的解密 ,得到實際數(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 實現(xiàn)TCP,UDP,Http服務端與客戶端

golang 實現(xiàn)UDP 服務端與客戶端

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

UDP服務端:

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.回復數(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服務器
	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實現(xiàn)tcp的服務端和客戶端

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

tcp 服務端

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()	//思考題:這里不填寫會有啥問題?
	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實現(xiàn)Http的服務端和客戶端

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

http服務端

var (
	Addr = ":8000"
)

// http的服務器
func main() {
	//1.創(chuàng)建路由器
	mux := http.NewServeMux()
	// 2. 設(shè)置路由規(guī)則
	mux.HandleFunc("/bye", sayBye)

	// 3.創(chuàng)建服務器
	server := &http.Server{
		Addr:         Addr,
		WriteTimeout: time.Second * 3,
		Handler:      mux,
	}
	// 4. 監(jiān)聽端口并提供服務
	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, // 超時時間
			KeepAlive: 30 * time.Second, //長連接時間
		}).DialContext,
		MaxIdleConns:          100,              //最大空閑連接數(shù)
		IdleConnTimeout:       90 * time.Second, // 空閑超時時間
		TLSHandshakeTimeout:   10 * time.Second, // tls握手超時時間
		ExpectContinueTimeout: 1 * time.Second,  // 100-continue 狀態(tài)碼超時時間
	}
	//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 服務器源碼解讀

  • 閱讀源代碼的原則:先整體在局部,先看腦圖在逐一分析
  • 注冊路由:理解函數(shù)是一等公民以及注冊原理
  • 開啟服務
  • 處理連接

函數(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)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MjVQl5gV-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017007628.png)]

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

到此這篇關(guān)于Go微服務網(wǎng)關(guān)的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go微服務網(wǎng)關(guān)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go?doudou開發(fā)gRPC服務快速上手實現(xiàn)詳解

    go?doudou開發(fā)gRPC服務快速上手實現(xiàn)詳解

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

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

    本文為大家介紹了Go習慣用法(多值賦值,短變量聲明和賦值,簡寫模式、多值返回函數(shù)、comma,ok 表達式、傳值規(guī)則)的基礎(chǔ)實例,幫大家鞏固扎實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出錯的原因及解決

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

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

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

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

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

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

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

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

    Go語言RPC Authorization進行簡單ip安全驗證的方法

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

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

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

    golang協(xié)程關(guān)閉踩坑實戰(zhàn)記錄

    協(xié)程(coroutine)是Go語言中的輕量級線程實現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-03-03

最新評論