golang重置,golang 文件同步

本文目錄一覽:

go語言實現一個簡單的簡單網關

網關=反向代理+負載均衡+各種策略,技術實現也有多種多樣,有基於 nginx 使用 lua 的實現,比如 openresty、kong;也有基於 zuul 的通用網關;還有就是 golang 的網關,比如 tyk。

這篇文章主要是講如何基於 golang 實現一個簡單的網關。

轉自: troy.wang/docs/golang/posts/golang-gateway/

整理:go語言鍾文文檔:

啟動兩個後端 web 服務(代碼)

這裡使用命令行工具進行測試

具體代碼

直接使用基礎庫 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy對象實現了serveHttp方法,因此可以直接作為 handler。

具體代碼

director中定義回調函數,入參為*http.Request,決定如何構造向後端的請求,比如 host 是否向後傳遞,是否進行 url 重寫,對於 header 的處理,後端 target 的選擇等,都可以在這裡完成。

director在這裡具體做了:

modifyResponse中定義回調函數,入參為*http.Response,用於修改響應的信息,比如響應的 Body,響應的 Header 等信息。

最終依舊是返回一個ReverseProxy,然後將這個對象作為 handler 傳入即可。

參考 2.2 中的NewSingleHostReverseProxy,只需要實現一個類似的、支持多 targets 的方法即可,具體實現見後面。

作為一個網關服務,在上面 2.3 的基礎上,需要支持必要的負載均衡策略,比如:

隨便 random 一個整數作為索引,然後取對應的地址即可,實現比較簡單。

具體代碼

使用curIndex進行累加計數,一旦超過 rss 數組的長度,則重置。

具體代碼

輪詢帶權重,如果使用計數遞減的方式,如果權重是5,1,1那麼後端 rs 依次為a,a,a,a,a,b,c,a,a,a,a…,其中 a 後端會瞬間壓力過大;參考 nginx 內部的加權輪詢,或者應該稱之為平滑加權輪詢,思路是:

後端真實節點包含三個權重:

操作步驟:

具體代碼

一致性 hash 算法,主要是用於分佈式 cache 熱點/命中問題;這裡用於基於某 key 的 hash 值,路由到固定後端,但是只能是基本滿足流量綁定,一旦後端目標節點故障,會自動平移到環上最近的那麼個節點。

實現:

具體代碼

每一種不同的負載均衡算法,只需要實現添加以及獲取的接口即可。

然後使用工廠方法,根據傳入的參數,決定使用哪種負載均衡策略。

具體代碼

作為網關,中間件必不可少,這類包括請求響應的模式,一般稱作洋蔥模式,每一層都是中間件,一層層進去,然後一層層出來。

中間件的實現一般有兩種,一種是使用數組,然後配合 index 計數;一種是鏈式調用。

具體代碼

Golang入門到項目實戰 | go語言常量

常量,就是在程序編譯階段就確定下來的值,而程序在運行時則無法改變該值。在Go程序中,常量可以是數值類型(包括整型、浮點型和複數類型)、布爾類型、字符串類型等。

定義一個常量使用const關鍵字,語法格式如下:

const:定義常量關鍵字

constantName:常量名稱

type:常量類型

value:常量的值

實例

運行結果

iota比較特殊,可以被認為是一個可被編譯器修改的常量,它默認開始值是0,每調用一次加1。遇到const關鍵字時被重置為0。

實例

運行結果

使用_跳過某些值

運行結果

運行結果

TCP那些事兒

目錄:

以前我也認為TCP是相當底層的東西,我永遠不需要去了解它。雖然差不多是這樣,但是實際生活中,你依然可能遇見和TCP算法相關的bug,這時候懂一些TCP的知識就至關重要了。( 本文也可以引申為,系統調用,操作系統這些都很重要,這個道理適用於很多東西 )

這裡推薦一篇小短文, 人人都應該懂點TCP

使用TCP協議通信的雙方必須先建立TCP連接,並在內核中為該連接維持一些必要的數據結構,比如連接的狀態、讀寫緩衝區、定時器等。當通信結束時,雙方必須關閉連接以釋放這些內核數據。TCP服務基於流,源源不斷從一端流向另一端,發送端可以逐位元組寫入,接收端可以逐位元組讀出,無需分段。

需要注意的幾點:

TCP狀態(11種):

eg.

以上為TCP三次握手的狀態變遷

以下為TCP四次揮手的狀態變遷

服務器通過 listen 系統調用進入 LISTEN 狀態,被動等待客戶端連接,也就是所謂的被動打開。一旦監聽到SYN(同步報文段)請求,就將該連接放入內核的等待隊列,並向客戶端發送帶SYN的ACK(確認報文段),此時該連接處於 SYN_RECVD 狀態。如果服務器收到客戶端返回的ACK,則轉到 ESTABLISHED 狀態。這個狀態就是連接雙方能進行全雙工數據傳輸的狀態。

而當客戶端主動關閉連接時,服務器收到FIN報文,通過返回ACK使連接進入 CLOSE_WAIT 狀態。此狀態表示——等待服務器應用程序關閉連接。通常,服務器檢測到客戶端關閉連接之後,也會立即給客戶端發送一個FIN來關閉連接,使連接轉移到 LAST_ACK 狀態,等待客戶端對最後一個FIN結束報文段的最後一次確認,一旦確認完成,連接就徹底關閉了。

客戶端通過 connect 系統調用主動與服務器建立連接。此系統調用會首先給服務器發一個SYN,使連接進入 SYN_SENT 狀態。

connect 調用可能因為兩種原因失敗:1. 目標端口不存在(未被任何進程監聽)護着該端口被 TIME_WAIT 狀態的連接佔用( 詳見後文 )。2. 連接超時,在超時時間內未收到服務器的ACK。

如果 connect 調用失敗,則連接返回初始的 CLOSED 狀態,如果調用成功,則轉到 ESTABLISHED 狀態。

客戶端執行主動關閉時,它會向服務器發送一個FIN,連接進入 TIME_WAIT_1 狀態,如果收到服務器的ACK,進入 TIME_WAIT_2 狀態。此時服務器處於 CLOSE_WAIT 狀態,這一對狀態是可能發生辦關閉的狀態(詳見後文)。此時如果服務器發送FIN關閉連接,則客戶端會發送ACK進行確認並進入 TIME_WAIT 狀態。

流量控制是為了控制發送方發送速率,保證接收方來得及接收。

接收方發送的確認報文中的窗口字段可以用來控制發送方窗口大小,從而影響發送方的發送速率。將窗口字段設置為 0,則發送方不能發送數據。

如果網絡出現擁塞,分組將會丟失,此時發送方會繼續重傳,從而導致網絡擁塞程度更高。因此當出現擁塞時,應當控制發送方的速率。這一點和流量控制很像,但是出發點不同。 流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網絡的擁塞程度。

TCP 主要通過四種算法來進行擁塞控制: 慢開始、擁塞避免、快重傳、快恢復。

在Linux下有多種實現,比如reno算法,vegas算法和cubic算法等。

發送方需要維護一個叫做擁塞窗口(cwnd)的狀態變量,注意擁塞窗口與發送方窗口的區別:擁塞窗口只是一個狀態變量,實際決定發送方能發送多少數據的是發送方窗口。

為了便於討論,做如下假設:

發送的最初執行慢開始,令 cwnd=1,發送方只能發送 1 個報文段;當收到確認後,將 cwnd 加倍,因此之後發送方能夠發送的報文段數量為:2、4、8 …

注意到慢開始每個輪次都將 cwnd 加倍,這樣會讓 cwnd 增長速度非常快,從而使得發送方發送的速度增長速度過快,網絡擁塞的可能也就更高。設置一個慢開始門限 ssthresh,當 cwnd = ssthresh 時,進入擁塞避免,每個輪次只將 cwnd 加 1。

如果出現了超時,則令 ssthresh = cwnd/2,然後重新執行慢開始。

在接收方,要求每次接收到報文段都應該對最後一個已收到的有序報文段進行確認。例如已經接收到 M1 和 M2,此時收到 M4,應當發送對 M2 的確認。

在發送方,如果收到三個重複確認,那麼可以知道下一個報文段丟失,此時執行快重傳,立即重傳下一個報文段。例如收到三個 M2,則 M3 丟失,立即重傳 M3。

在這種情況下,只是丟失個別報文段,而不是網絡擁塞。因此執行快恢復,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此時直接進入擁塞避免。

慢開始和快恢復的快慢指的是 cwnd 的設定值,而不是 cwnd 的增長速率。慢開始 cwnd 設定為 1,而快恢復 cwnd 設定為 ssthresh。

  發送端的每個TCP報文都必須得到接收方的應答,才算傳輸成功。

  TCP為每個TCP報文段都維護一個重傳定時器。

  發送端在發出一個TCP報文段之後就啟動定時器,如果在定時時間類未收到應答,它就將重發該報文段並重置定時器。

  因為TCP報文段最終在網絡層是以IP數據報的形式發送,而IP數據報到達接收端可能是亂序或者重複的。TCP協議會對收到的TCP報文進行重排、整理,確保順序正確。

TCP報文段所攜帶的應用程序數據按照長度分為兩種: 交互數據 和 成塊數據

對於什麼是粘包、拆包問題,我想先舉兩個簡單的應用場景:

對於第一種情況,服務端的處理流程可以是這樣的:當客戶端與服務端的連接建立成功之後,服務端不斷讀取客戶端發送過來的數據,當客戶端與服務端連接斷開之後,服務端知道已經讀完了一條消息,然後進行解碼和後續處理…。對於第二種情況,如果按照上面相同的處理邏輯來處理,那就有問題了,我們來看看 第二種情況 下客戶端發送的兩條消息遞交到服務端有可能出現的情況:

第一種情況:

服務端一共讀到兩個數據包,第一個包包含客戶端發出的第一條消息的完整信息,第二個包包含客戶端發出的第二條消息,那這種情況比較好處理,服務器只需要簡單的從網絡緩衝區去讀就好了,第一次讀到第一條消息的完整信息,消費完再從網絡緩衝區將第二條完整消息讀出來消費。

第二種情況:

服務端一共就讀到一個數據包,這個數據包包含客戶端發出的兩條消息的完整信息,這個時候基於之前邏輯實現的服務端就蒙了,因為服務端不知道第一條消息從哪兒結束和第二條消息從哪兒開始,這種情況其實是發生了TCP粘包。

第三種情況:

服務端一共收到了兩個數據包,第一個數據包只包含了第一條消息的一部分,第一條消息的後半部分和第二條消息都在第二個數據包中,或者是第一個數據包包含了第一條消息的完整信息和第二條消息的一部分信息,第二個數據包包含了第二條消息的剩下部分,這種情況其實是發送了TCP拆,因為發生了一條消息被拆分在兩個包裏面發送了,同樣上面的服務器邏輯對於這種情況是不好處理的。

我們知道tcp是以流動的方式傳輸數據,傳輸的最小單位為一個報文段(segment)。tcp Header中有個Options標識位,常見的標識為mss(Maximum Segment Size)指的是,連接層每次傳輸的數據有個最大限制MTU(Maximum Transmission Unit),一般是1500比特,超過這個量要分成多個報文段,mss則是這個最大限制減去TCP的header,光是要傳輸的數據的大小,一般為1460比特。換算成位元組,也就是180多位元組。

tcp為提高性能,發送端會將需要發送的數據發送到緩衝區,等待緩衝區滿了之後,再將緩衝中的數據發送到接收方。同理,接收方也有緩衝區這樣的機制,來接收數據。

發生TCP粘包、拆包主要是由於下面一些原因:

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

寫了一個簡單的 golang 版的tcp服務器實例,僅供參考:

例子

參考和推薦閱讀書目:

注釋:

eg.

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/188320.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-28 13:29
下一篇 2024-11-28 13:29

相關推薦

發表回復

登錄後才能評論