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://img.jbzj.com/file_images/article/202207/2022071109541544.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)]](http://img.jbzj.com/file_images/article/202207/2022071109541651.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)]](http://img.jbzj.com/file_images/article/202207/2022071109541654.png)
為啥會出現(xiàn)粘包,拆包,如何處理
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5vWmWpIu-1648194571603)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1647791724023.png)]](http://img.jbzj.com/file_images/article/202207/2022071109541655.png)
粘包、拆包表現(xiàn)形式
現(xiàn)在假設(shè)客戶端向服務端連續(xù)發(fā)送了兩個數(shù)據(jù)包,用packet1和packet2來表示,那么服務端收到的數(shù)據(jù)可以分為三種,現(xiàn)列舉如下:
第一種情況,接收端正常收到兩個數(shù)據(jù)包,即沒有發(fā)生拆包和粘包的現(xiàn)象,此種情況不在本文的討論范圍內(nèi)。

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

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

產(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)]](http://img.jbzj.com/file_images/article/202207/2022071109541759.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)]](http://img.jbzj.com/file_images/article/202207/2022071109541860.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)]](http://img.jbzj.com/file_images/article/202207/2022071109541861.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://img.jbzj.com/file_images/article/202207/2022071109541862.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)]](http://img.jbzj.com/file_images/article/202207/2022071109541863.png)
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7JakmfrM-1648194571606)(C:\Users\咩咩\AppData\Roaming\Typora\typora-user-images\1648017357816.png)]](http://img.jbzj.com/file_images/article/202207/2022071109541864.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)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
Go習慣用法(多值賦值短變量聲明賦值簡寫模式)基礎(chǔ)實例
本文為大家介紹了Go習慣用法(多值賦值,短變量聲明和賦值,簡寫模式、多值返回函數(shù)、comma,ok 表達式、傳值規(guī)則)的基礎(chǔ)實例,幫大家鞏固扎實Go語言基礎(chǔ)2024-01-01
Go 使用Unmarshal將json賦給struct出錯的原因及解決
這篇文章主要介紹了Go 使用Unmarshal將json賦給struct出錯的原因及解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
Go語言RPC Authorization進行簡單ip安全驗證的方法
這篇文章主要介紹了Go語言RPC Authorization進行簡單ip安全驗證的方法,實例分析了Go語言進行ip驗證的技巧,需要的朋友可以參考下2015-03-03
golang協(xié)程關(guān)閉踩坑實戰(zhàn)記錄
協(xié)程(coroutine)是Go語言中的輕量級線程實現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03

